PlannerModule.psm1

<#
     
    ===========================================================================
    Created on: 2019-06-03
         Updated on: 2020-06-06
    Created by: Zeng Yinghua
    Filename: PlannerModule.psm1
    -------------------------------------------------------------------------
    Module Name: PlannerModule
 
    Histrory:
    1.0.2.5 - (2020-06-06) Merged Authendicate fix from Alexey Dolgopolov, added alias for old funtion names
    1.0.2.0 - (2019-07-25) Added $Credential parameter for authdentication
    1.0.0.0 - (2019-06-03) First version
    ===========================================================================
 
#>


#Replace some of the old function names
New-Alias -Name "Update-PlannerModuelEnvironment" Update-PlannerModuleEnvironment
New-Alias -Name "Invoke-ListUnifiedGroups" Get-UnifiedGroupsList

function Get-PlannerAuthToken
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    Param
    (
        [parameter(Mandatory = $false, HelpMessage = "Specify a PSCredential object containing username and password.")]
        [ValidateNotNullOrEmpty()]
        [PSCredential]$Credential
    )
    
    Write-Host "Checking for AzureAD module..."
    # Always consider to select the latest version
    $AadModule = Get-Module -Name "AzureAD" -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
    if ($AadModule -eq $null)
    {
        Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview"
        $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
    }
    if ($AadModule -eq $null)
    {
        Write-Error "AzureAD Powershell module not installed..."
        Write-Error "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt"
        return
    }
    
    # Getting path to ActiveDirectory Assemblies

    $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
    $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

    
    [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
    [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
    
    if ($PlannerEnvUpdated -ne $true)
    {
        $clientId = "3556cd23-09eb-42b3-a3b9-72cba5c7926e"
        $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
    }
    $resourceAppIdURI = "https://graph.microsoft.com"
    $authority = "https://login.microsoftonline.com/common"
    
    try
    {
        $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
        
        # https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx
        # Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession
        
        $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Always"
        
        if (!$Credential)
        {
            $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters).Result
        }
        else
        {
            # Construct required identity model credential
            $UserPasswordCredential = New-Object -TypeName "Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential" -ArgumentList ($Credential.UserName, $Credential.Password) -ErrorAction Stop
            
            # Acquire access token
            $authResult = ([Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($authContext, $resourceAppIdURI, $clientId, $UserPasswordCredential)).Result
        }
        
        # If the accesstoken is valid then create the authentication header
        if ($authResult.AccessToken)
        {
            # Creating header for Authorization token
            $authHeader = @{
                'Content-Type'  = 'application/json'
                'Authorization' = "Bearer " + $authResult.AccessToken
                'ExpiresOn'        = $authResult.ExpiresOn
            }
            return $authHeader
        }
        else
        {
            Write-Error "Authorization Access Token is null, please re-run authentication..."
            break
        }
    }
    catch
    {
        Write-Error $_.Exception.Message
        Write-Error $_.Exception.ItemName
        break
    }
}

function Update-PlannerModuleEnvironment
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory = $false)]
        [string]$ClientId,
        [string]$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
    )
    
    Write-Warning "WARNING: Call the 'Connect-Planner' cmdlet to use the updated environment parameters."
    
    Write-Host "
    AuthUrl : https://login.microsoftonline.com/common
    ResourceId : https://graph.microsoft.com
    GraphBaseAddress : https://graph.microsoft.com
    AppId : $ClientId
    RedirectLink : $redirectUri
    SchemaVersion : beta
 
"
 -ForegroundColor Cyan
    
    $Script:ClientId = $($ClientId)
    $Script:PlannerEnvUpdated = $true
}

Function Get-UnifiedGroupsList
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/Groups?`$filter=groupTypes/any(c:c+eq+'Unified')"
        (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function New-AADUnifiedGroup
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory = $True)]
        [string]$GroupName,
        [Parameter(Mandatory = $True)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet("Public", "Private")]
        $visibility = "Private"
    )
    
    $randomNum = (Get-Random -Maximum 1000).tostring()
    $mailNickname = $GroupName.Replace(" ", "") + $randomNum
    
    try
    {
        $Body = @"
{
  "description": "$($GroupName)",
  "displayName": "$($GroupName)",
  "groupTypes": [
    "Unified"
  ],
  "mailEnabled": true,
  "mailNickname": "$($mailNickname)",
  "securityEnabled": false,
  "visibility": "Private"
}
"@

        
        $uri = "https://graph.microsoft.com/beta/groups"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
        Write-Host "$($GroupName) is created, Group visibility type is $($visibility)" -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function Add-AADUnifiedGroupMember
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory = $True)]
        [string]$GroupID,
        [Parameter(Mandatory = $True)]
        [array]$UserPrincipalNames
    )
    
    foreach ($UserPrincipalName in $UserPrincipalNames)
    {
        try
        {
            
            #Get users id
            $userID = (Get-AADUserDetails -UserPrincipalName $UserPrincipalName).id
            
            $Body = @"
{
  "@odata.id": "https://graph.microsoft.com/beta/directoryObjects/$($userID)"
}
"@

            $uri = "https://graph.microsoft.com/beta/groups/$GroupID/members/`$ref"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
            Write-Host "$($UserPrincipalNames) is added to GroupID: $($GroupID)" -ForegroundColor Cyan
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
    
}

Function Get-PlannerPlanGroup
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [cmdletbinding()]
    param
    (
        [string]$GroupName
    )
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/Groups?`$filter=groupTypes/any(c:c+eq+'Unified')+and+displayName+eq+`'$Groupname`'"
        (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function Invoke-ListPlannerPlans
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'GroupID')]
        [Alias("id")]
        $GroupID,
        [Parameter(Mandatory = $True, ParameterSetName = 'GroupName')]
        $GroupName
    )
    
    Write-Warning "This is an old function, please use 'Get-PlannerPlansList' instead"
    
    Process
    {
        try
        {
            switch ($PsCmdlet.ParameterSetName)
            {
                'GroupName'
                {
                    $GroupID = (Get-PlannerPlanGroup -GroupName $($GroupName)).id
                }
            }
            $Uri = "https://graph.microsoft.com/beta/groups/$GroupID/planner/plans"
            (Invoke-RestMethod -uri $Uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerPlansList
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'GroupID')]
        [Alias("id")]
        $GroupID,
        [Parameter(Mandatory = $True, ParameterSetName = 'GroupName')]
        $GroupName
    )
    
    Process
    {
        try
        {
            switch ($PsCmdlet.ParameterSetName)
            {
                'GroupName'
                {
                    $GroupID = (Get-PlannerPlanGroup -GroupName $($GroupName)).id
                }
            }
            $Uri = "https://graph.microsoft.com/beta/groups/$GroupID/planner/plans"
            (Invoke-RestMethod -uri $Uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerPlan
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$PlanID
    )
    
    Process
    {
        try
        {
            $uri = "https://graph.microsoft.com/beta/planner/plans/$PlanID"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Invoke-ListPlannerPlanTasks
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$PlanID
    )
    
    Write-Host "This is an old function, please use 'Get-PlannerPlanTasks' instead"
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/plans/$PlanID/tasks"
            (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
    
}

Function Get-PlannerPlanTasks
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$PlanID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/plans/$PlanID/tasks"
            (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
    
}

Function Invoke-ListPlannerPlanBuckets
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$PlanID
    )
    
    Write-Warning "This is an old function, please use 'Get-PlannerPlanBuckets' instead"
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/plans/$PlanID/buckets"
            (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerPlanBuckets
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$PlanID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/plans/$PlanID/buckets"
            (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerTask
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$TaskID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/tasks/$TaskID"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerTaskDetails
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$TaskID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta//planner/tasks/$TaskID/details"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerPlanDetails
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$PlanID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/plans/$PlanID/details"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerBucket
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$BucketID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/buckets/$BucketID"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Invoke-ListPlannerBucketTasks
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$BucketID
    )
    
    Write-Warning "This is an old function, please use 'Get-PlannerBucketTasksList' instead"
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/buckets/$BucketID/tasks"
            (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerBucketTasksList
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$BucketID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/buckets/$BucketID/tasks"
            (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerAssignedToTaskBoardTaskFormat
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$TaskID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/tasks/$TaskID/assignedToTaskBoardFormat"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerBucketTaskBoardTaskFormat
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$TaskID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/tasks/$TaskID/bucketTaskBoardFormat"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Get-PlannerProgressTaskBoardTaskFormat
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        [string[]]$TaskID
    )
    
    Process
    {
        try
        {
            
            $uri = "https://graph.microsoft.com/beta/planner/tasks/$TaskID/progressTaskBoardFormat"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function New-PlannerPlan
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        
        [Parameter(Mandatory = $true)]
        $PlanName,
        [Parameter(Mandatory = $True)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet("Public", "Private")]
        $visibility = "Private"
    )
    
    
    $GroupInfo = Get-PlannerPlanGroup -GroupName $($PlanName) -ErrorAction SilentlyContinue
    if ($GroupInfo)
    {
        Write-Warning "Same Group name $($PlanName) is found, please use 'New-PlannerPlanToGroup' to create add plan to Group, or change plan name"
        break
    }
    else
    {
        $results = New-AADUnifiedGroup -GroupName $($PlanName) -visibility $($visibility)
        $GroupID = $results.id
        Start-Sleep 10
        #Get current user
        $uri = "https://graph.microsoft.com/beta/me"
        $UserPrincipalName = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method GET).UserPrincipalName
        Add-AADUnifiedGroupMember -GroupID $GroupID -UserPrincipalName $UserPrincipalName
        Start-Sleep 10
    }
    
    
    $Body = @"
{
  "owner": "$($GroupID)",
  "title": "$($PlanName)"
}
"@

    
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/plans"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
        Write-Host "$($PlanName) is created. Group visibility is $($visibility)" -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
}

Function New-PlannerPlanToGroup
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        
        [Parameter(Mandatory = $true)]
        $PlanName,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $True)]
        [Alias("id")]
        $GroupID
    )
    
    #Add current user to group
    #Get current user
    $uri = "https://graph.microsoft.com/beta/me"
    $UserID = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method GET).id
    $uri = "https://graph.microsoft.com/beta/Groups/$GroupID/members?`$filter=id eq '$UserID'"
    
    try
    {
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method GET
    }
    catch
    {
        $AddUser = $true
    }
    if ($AddUser -eq $true)
    {
        try
        {
            $Body = @"
{
  "@odata.id": "https://graph.microsoft.com/beta/directoryObjects/$($userID)"
}
"@

            
            $uri = "https://graph.microsoft.com/beta/groups/$GroupID/members/`$ref"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
            Start-Sleep 10
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "$($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
    
    $Body = @"
{
  "owner": "$($GroupID)",
  "title": "$($PlanName)"
}
"@

    
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/plans"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
        Write-Host "$($PlanName) is created." -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
}

Function New-PlannerBucket
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'PlanID')]
        [Alias("id")]
        $PlanID,
        [Parameter(Mandatory = $True)]
        $BucketName
    )
    
    Process
    {
        $Body = @"
{
  "name": "$($BucketName)",
  "planId": "$($PlanID)",
  "orderHint": " !"
}
"@

        
        try
        {
            $uri = "https://graph.microsoft.com/beta/planner/buckets"
            Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
            Write-Host "$($BucketName) is created." -ForegroundColor Cyan
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function New-PlannerTask
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'PlanID')]
        [Alias("id")]
        $PlanID,
        [Parameter(Mandatory = $True)]
        $TaskName,
        [Parameter(Mandatory = $False)]
        $BucketID,
        [Parameter(Mandatory = $False, HelpMessage = "DateTime format needs to be YYYY-MM-DD, example 2019-06-30")]
        [DateTime]$startDate,
        [Parameter(Mandatory = $False, HelpMessage = "DateTime format needs to be YYYY-MM-DD, example 2019-06-30")]
        [DateTime]$dueDate
    )
    
    #user defualt bucket name To do
    If (!$BucketID)
    {
        $BucketID = (Get-PlannerPlanBuckets -PlanID $($PlanID) | Where-Object { $_.name -like 'To do' }).id
    }
    
    #if start date and due date is not definde
    if (!$startDate -and !$dueDate)
    {
        $Body = @"
{
  "planId": "$($PlanID)",
  "bucketId": "$($BucketID)",
  "title": "$($TaskName)",
}
"@

    }
    
    #if start date is due date are definded
    if ($startDate -and $dueDate)
    {
        $startDateformat = $startDate.ToString("yyyy-MM-ddT10:00:00Z")
        $dueDateformat = $dueDate.ToString("yyyy-MM-ddT10:00:00Z")
        
        $Body = @"
{
    "planId": "$($PlanID)",
    "bucketId": "$($BucketID)",
    "title": "$($TaskName)",
    "startDateTime": "$($startDateformat)",
    "dueDateTime": "$($dueDateformat)"
}
"@

    }
    
    #if no start date, but has duedate
    if (!$startDate -and $dueDate)
    {
        $dueDateformat = $dueDate.ToString("yyyy-MM-ddT10:00:00Z")
        
        $Body = @"
{
  "planId": "$($PlanID)",
  "bucketId": "$($BucketID)",
  "title": "$($TaskName)",
  "dueDateTime": "$($dueDateformat)"
}
"@

    }
    
    #if has start date, but no due date
    if ($startDate -and !$dueDate)
    {
        $startDateformat = $startDate.ToString("yyyy-MM-ddT10:00:00Z")
        
        $Body = @"
{
  "planId": "$($PlanID)",
  "bucketId": "$($BucketID)",
  "title": "$($TaskName)",
  "startDateTime": "$($startDateformat)"
}
"@

    }
    
    #Make graph call
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/tasks"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method POST -Body $Body
        Write-Host "$($TaskName) is created." -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
}

Function Get-AADUserDetails
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True)]
        $UserPrincipalName
    )
    
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/users?`$filter=userPrincipalName eq '$($UserPrincipalName)'"
        (Invoke-RestMethod -Uri $uri -Headers $authToken -Method GET).value
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
}

Function Invoke-AssignPlannerTask
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'TaskID')]
        [Alias("id")]
        $TaskID,
        [Parameter(Mandatory = $True)]
        [array]$UserPrincipalNames
    )
    
    foreach ($UserPrincipalName in $UserPrincipalNames)
    {
        #Get users id
        $userID = (Get-AADUserDetails -UserPrincipalName $UserPrincipalName).id
        
        #Get Task details
        $respond = Get-PlannerTask -TaskID $TaskID
        $ETag = $respond.'@odata.etag'
        $TaskTile = $respond.title
        
        $Body = @"
{
  "assignments": {
    "$($userID)": {
        "@odata.type": "#microsoft.graph.plannerAssignment",
        "orderHint": " !"
    }
  }
}
"@

        #Add if-match to new tocket header
        $NewToken = $authToken.Clone()
        $NewToken.add("If-Match", $ETag)
        
        try
        {
            $uri = "https://graph.microsoft.com/beta/planner/tasks/$($TaskID)"
            Invoke-RestMethod -Uri $uri -Headers $NewToken -Method PATCH -Body $Body
            Write-Host "$($UserPrincipalNames) is assigned to Task: $($TaskTile)" -ForegroundColor Cyan
        }
        catch
        {
            $ex = $_.Exception
            if ($($ex.Response.StatusDescription) -match 'Unauthorized')
            {
                Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
            }
            Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
            break
        }
    }
}

Function Update-PlannerPlanCategories
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'PlanID')]
        [Alias("id")]
        $PlanID,
        [Parameter(Mandatory = $false)]
        $category1 = 'null',
        $category2 = 'null',
        $category3 = 'null',
        $category4 = 'null',
        $category5 = 'null',
        $category6 = 'null'
    )
    
    #Get Plan details
    $respond = Get-PlannerPlanDetails -PlanID $PlanID
    $ETag = $respond.'@odata.etag'
    
    $Body = @"
{
  "categoryDescriptions": {
    "category1": "$category1",
    "category2": "$category2",
    "category3": "$category3",
    "category4": "$category4",
    "category5": "$category5",
    "category6": "$category6"
  }
}
"@

    
    #Add if-match to new tocket header
    $NewToken = $authToken.Clone()
    $NewToken.add("If-Match", $ETag)
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/plans/$($PlanID)/details"
        Invoke-RestMethod -Uri $uri -Headers $NewToken -Method PATCH -Body $Body
        Write-Host "Categories/Lables are updated" -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function Invoke-AssignPlannerTaskCategories
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'TaskID')]
        [Alias("id")]
        $TaskID,
        [Parameter(Mandatory = $True)]
        [bool]$category1 = $false,
        [bool]$category2 = $false,
        [bool]$category3 = $false,
        [bool]$category4 = $false,
        [bool]$category5 = $false,
        [bool]$category6 = $false
    )
    
    #Get Task details
    $respond = Get-PlannerTask -TaskID $TaskID
    $ETag = $respond.'@odata.etag'
    $TaskName = $respond.title
    
    $Body = @"
{
  "appliedCategories": {
    "category1": $($category1.tostring().ToLower()),
    "category2": $($category2.tostring().ToLower()),
    "category3": $($category3.tostring().ToLower()),
    "category4": $($category4.tostring().ToLower()),
    "category5": $($category5.tostring().ToLower()),
    "category6": $($category6.tostring().ToLower())
  }
}
"@

    
    #Add if-match to new tocket header
    $NewToken = $authToken.Clone()
    $NewToken.add("If-Match", $ETag)
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/tasks/$($TaskID)"
        Invoke-RestMethod -Uri $uri -Headers $NewToken -Method PATCH -Body $Body
        Write-Host "Categories are assigned to Task: $($TaskName)" -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function Add-PlannerTaskDescription
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'TaskID')]
        [Alias("id")]
        $TaskID,
        [Parameter(Mandatory = $True)]
        [string[]]$Description
    )
    
    #Get Task details
    $respond = Get-PlannerTaskDetails -TaskID $TaskID
    $ETag = $respond.'@odata.etag'
    
    $Body = @"
{
    "description": "$($Description)"
}
"@

    
    #Add if-match to new tocket header
    $NewToken = $authToken.Clone()
    $NewToken.add("If-Match", $ETag)
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/tasks/$($TaskID)/Details"
        Invoke-RestMethod -Uri $uri -Headers $NewToken -Method PATCH -Body $Body
        Write-Host "Task Description is updated" -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function Add-PlannerTaskChecklist
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True, ParameterSetName = 'TaskID')]
        [Alias("id")]
        $TaskID,
        [Parameter(Mandatory = $True)]
        [string[]]$Title,
        [bool]$IsChecked = $false
    )
    
    #Get Task details
    $respond = Get-PlannerTaskDetails -TaskID $TaskID
    $ETag = $respond.'@odata.etag'
    
    $checklist = (New-Guid).Guid
    
    $Body = @"
{
    "checklist": {
        "$checklist": {
            "@odata.type": "#microsoft.graph.plannerChecklistItem",
            "isChecked": $($IsChecked.tostring().ToLower()),
            "title": "$($Title)"
        }
    }
}
"@

    
    #Add if-match to new tocket header
    $NewToken = $authToken.Clone()
    $NewToken.add("If-Match", $ETag)
    
    try
    {
        $uri = "https://graph.microsoft.com/beta/planner/tasks/$($TaskID)/Details"
        Invoke-RestMethod -Uri $uri -Headers $NewToken -Method PATCH -Body $Body
        Write-Host "CheckList is added" -ForegroundColor Cyan
    }
    catch
    {
        $ex = $_.Exception
        if ($($ex.Response.StatusDescription) -match 'Unauthorized')
        {
            Write-Error "Unauthorized, Please check your permissions and use the 'Connect-Planner' command to authenticate"
        }
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        break
    }
    
}

Function Connect-Planner
{
    # .ExternalHelp PlannerModule.psm1-Help.xml
    
    [cmdletbinding(DefaultParameterSetName='$ForceInteractive')]
    Param
    (
        
        [Parameter(Mandatory = $false, ParameterSetName = "AuthPrompt")]
        [Switch] $ForceInteractive,
        [parameter(Mandatory = $false, ParameterSetName = "AuthCredential", HelpMessage = "Specify a PSCredential object containing username and password.")]
        [ValidateNotNullOrEmpty()]
        [PSCredential]$Credential
    )
    
    
    #Authentication
    if ($Credential)
    {
        # Getting the authorization token
        $Script:authToken = Get-PlannerAuthToken -Credential $Credential
    }
    else {
    
        if ($ForceInteractive -eq $true)
        {
            # Getting the authorization token
            $Script:authToken = Get-PlannerAuthToken
            return $authToken
        }
    
        if ($ForceInteractive -eq $false)
        {
        
            # Checking if authToken exists before running authentication
            if ($Script:authToken)
            {
                # Setting DateTime to Universal time to work in all timezones
                $DateTime = (Get-Date).ToUniversalTime()
                # If the authToken exists checking when it expires
                $TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes
                if ($TokenExpires -le 0)
                {
                    Write-Host "Authentication Token expired" $TokenExpires "minutes ago"
                    $Script:authToken = Get-PlannerAuthToken
                }
            }
            # Authentication doesn't exist, calling Get-AuthToken function
            else
            {
                # Getting the authorization token
                $Script:authToken = Get-PlannerAuthToken
            }
        }
    }
    
}