Share via


Assignments Scripts - Delete

Below are the PowerShell scripts for Delete - EDU assignments, Exports - EDU assignments and Get - user classes.

Delete - EDU assignments

<#
   .Synopsis
    Delete the Edu Assignments data of a user in a set of classes specified in csv file.
    .DESCRIPTION
    The script reads the class details of user from input csv file. Get the assignments and submissions of each assignment and then delete the submissions, it also generate a report file(DeleteAssignmentsReport.csv) for extra information of call information. when the deletion of submission is done it updates the DeleteSubmissionsProcessed column to true for that specific class. If the script fails in between we can rerun the script with the same input file, because the file will be updated for last deletion of submission.
   .Example
    .\Delete-EduAssignments.ps1 -userClassDetailsFile <full path of user class details csv file>
    This will delete all the submissions but does not remove the user info from class
    .\Delete-EduAssignments.ps1 -userClassDetailsFile <full path of user class details csv file> -removeMemberFromClass
    This will delete all the submissions and remove the user info from class
   .Parameter userClassDetailsFile
   This is the csv file(userClassDetails.csv) which is the output of Get-UserClasses.ps1 script. This file have information about the user classes and whether the deletion and get submissions are processed. This value should be full path of the file(drive:\GDPR\userClassDetails.csv)
   .Switch Parameter removeMemberFromClass
   When we use this parameter it will remove the user info from each class
   .Notes
   We need to have the Microsoft.IdentityModel.Clients.ActiveDirectory.dll and Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll are required
#>
param(
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({
        if(-Not ($_ | Test-Path)) {
            throw "The userClassDetailsFile does not exist."
        }
        if(-Not ($_ | Test-Path -PathType Leaf) ){
            throw "The userClassDetailsFile argument must be a file. Folder paths are not allowed."
        }
        if($_ -notmatch "(\.csv)"){
            throw "The userClassDetailsFile should be a comma-separated file, generated using Get-UserClasses.ps1"
        }
        return $true
    })]
    [string] $userClassDetailsFile,
    [parameter(Mandatory=$false)]
    [switch] $removeMemberFromClass
)
# Load ADAL
Add-Type -Path ".\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$educationEndpoint = "https://graph.microsoft.com/beta/education/"
$script:userClassDetails = @()
$script:deleteAssignmentsReport = @()
$script:maxRetryAttempt = 3
$script:authHeaders = $null
$script:authenticationResult = $null
$graphEndpoint = "https://graph.microsoft.com"
$authString = "https://login.windows.net/common"
#Get the authheaders
function Get-AuthHeaders
{
    Param(
        [Parameter(Mandatory=$false)]
        [bool] $useRefreshToken = $false
    )
    $clientId = "eb2298a1-a6bb-4f16-a27a-b582815db47b"
    $redirectURI = New-Object System.Uri("urn:ietf:wg:oauth:2.0:oob")
    $promptBehavior = [Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always
    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authString
    if($useRefreshToken -eq $false)
    {
        $script:authenticationResult = $authContext.AcquireToken($graphEndpoint, $clientID, $redirectURI, $promptBehavior)
    }
    else
    {
        $script:authenticationResult = $authContext.AcquireTokenByRefreshToken($script:authenticationResult.RefreshToken, $clientId)
    }
    $authHeader = $script:authenticationResult.AccessTokenType + " " + $script:authenticationResult.AccessToken
    $headers = @{"Authorization" = $authHeader; "Content-Type" = "Application/json"}
    return $headers
}
#Retry logic and logging of errors for script
function Invoke-RequestWithRetry
{
    param(
        [Parameter(Mandatory=$true)]$url,
        [Parameter(Mandatory=$true)][ValidateSet('Get','Delete', ignorecase=$true)]$httpmethod,
        [Parameter(Mandatory=$false)]$classId,
        [Parameter(Mandatory=$false)]$className,
        [Parameter(Mandatory=$true)]$OperationName
    )
    for($i=1; $i -le $script:maxRetryAttempt; $i++)
    {
        try
        {
            $APIResult = Invoke-WebRequest -Method $httpmethod -Uri $url -Headers $script:authHeaders
            $resultCount = 0
            if($OperationName -eq "GetAssignments")
            {
                $valueStartIndex = ($APIResult.content.indexof('"value":') + 8)
                $valueEndIndex = $APIResult.content.Length
                $valueString = $APIResult.content.substring($valueStartIndex, $valueEndIndex-$valueStartIndex -1)
                $resultCount = (($valuestring | select-string '"classId":' -AllMatches ).Matches.Count)
            }
            else
            {
                $submissionsJson = $APIResult.content | ConvertFrom-Json
                $resultCount = $submissionsJson.value.count
            }
            $script:deleteAssignmentsReport += [PSCustomObject]@{
                RequestUrl = $url
                Method = $httpmethod
                ResponseCode = $APIResult.StatusCode
                ClassName = $className
                ClassId = $classId
                RequestId = $APIResult.Headers["request-id"]
                StatusDescription = $APIResult.StatusDescription
                NumberOfAttempts = $i
                OperationName = $OperationName
                ResultCount = $resultCount
            }
            return $APIResult
        }
        catch
        {
            if($_.Exception.Response -ne $null)
            {
                $responseCode = $_.Exception.Response.StatusCode.Value__
                $requestId = $_.Exception.Response.Headers["request-id"]
            }
           
            $script:deleteAssignmentsReport += [PSCustomObject]@{
                RequestUrl = $url
                Method = $httpmethod
                ResponseCode = $responseCode
                ClassName = $className
                ClassId = $classId
                RequestId = $requestId
                StatusDescription = $_.Exception.Message
                NumberOfAttempts = $i
                OperationName = $OperationName
                ResultCount = $resultCount
            }
            if($i -eq $script:maxRetryAttempt)
            {
                throw $_
            }
            if($responseCode -eq 401)
            {
                $script:authHeaders = Get-AuthHeaders -useRefreshToken $true
            }
        }
    }
}
#Remove member from class
function Remove-MemberFromClass
{
    param(
        [Parameter(Mandatory=$true)]$classDetails
    )
    $removeMemberUrl = "{0}/v1.0/groups/{1}/members/{2}/`$ref" -f $graphEndpoint, $($classDetails.ClassId), $($classDetails.UserId)
    try
    {
        Invoke-RequestWithRetry -url $removeMemberUrl -httpmethod Delete -classId $($classDetails.ClassId) -className $($classDetails.ClassName) -OperationName "RemoveMemberfromClass" | Out-Null   
    }
    catch
    {
        throw $_
    }
   
}
function Delete-Assignments
{
    param(
        [Parameter(Mandatory=$true)]$classDetail
    )
    try
    {
        $classId = $classDetail.ClassId
        $className = $classDetail.ClassName
        $userId = $classDetail.UserId
        $role = $classDetail.Role
        $getAssignmentsUri = ("{0}/classes('{1}')/assignments?TargetUserId={2}&UserClassRole={3}" -f $educationEndpoint, $classId, $userId, $role)
        $assignments = $null
        $assignments = Invoke-RequestWithRetry -url $getAssignmentsUri -httpmethod Get -classId $classId -className $className -OperationName "GetAssignments"
        if($assignments -ne $null -and $assignments.content -ne $null -and $role -eq "Student"))
        {
            $assignmentsCount = ((($assignments.content) | ConvertFrom-Json).value).count
            Write-Host "Retrieved $assignmentsCount Assignments for $className class"
            $assignmentsTracker = 0
            foreach($assignment in (($assignments.content) | ConvertFrom-Json).value)
            {
                $assignmentName = $assignment.displayName
                $assignmentId = $assignment.id
                $submissions = $assignment.submissions
                Write-Host "Deleting submissions for $($assignmentName)"
                $submissionsTracker = 0
                foreach($submission in $submissions)
                {
                    $deleteSDSSubmissionUri = "{0}/beta/education/classes/{1}/assignments/{2}/submissions/{3}?TargetUserId={4}&UserClassRole=Student" -f $graphEndpoint, $classId, $assignmentId, $($submission.id), $userId
                    if(($submissionsTracker -eq ($submissions.Length -1)) -and ($assignmentsTracker -eq ($assignmentsCount - 1)) -and ($removeMemberFromClass.IsPresent))
                    {
                        Write-Host "Deleting submission and classinfo for user"
                        $deleteSDSSubmissionUri = $deleteSDSSubmissionUri + "&CleanupUserClassData=true"
                    }
                    $deleteResponse = Invoke-RequestWithRetry -url $deleteSDSSubmissionUri -httpmethod Delete -classId $classId -className $className -OperationName "DeleteSubmission"
                    if($deleteResponse -ne $null -and $deleteResponse.Content.Length -ne 0 -and $submissionsTracker -eq ($submissions.Length -1))
                    {
                        $classDetail.DeleteSubmissionsProcessed = "True"
                        $script:userClassDetails | Export-Csv -Path $($directoryPathOfFile) -NoTypeInformation -Force
                    }
                    $submissionsTracker++
                }
                $assignmentsTracker++
            }
        }
    }
    catch
    {   
        throw $_
    }
}
$directoryPathOfFile = (Get-Item -path $($userClassDetailsFile)).FullName
$script:authHeaders = Get-AuthHeaders
$script:userClassDetails = import-csv $userClassDetailsFile
$progressTracker = 0;
foreach($classDetail in $script:userClassDetails)
{
    Write-Progress -Activity "Deleting Assignments for user" -Status "Processing for class: $classDetail.classId" -PercentComplete ($progressTracker/$script:userClassDetails.count * 100)
    if($classDetail.DeleteSubmissionsProcessed -eq "False")
    {
        try
        {
            if($removeMemberFromClass.IsPresent)
            {
                Write-Host "Deleting user from $($classDetail.ClassName)"
                Remove-MemberFromClass -classDetails $classDetail
            }
            Write-host "Deleting Assignments of user for $($classDetails.ClassName)"
            Delete-Assignments -classDetails $classDetail  
        }
        catch
        {
            write-Error $_.Exception.Message   
        }
    }
    $progressTracker++
}
$directoryPath = (Get-Item -Path ".\" -Verbose).FullName
$script:deleteAssignmentsReport | Export-Csv -Path .\DeleteAssignmentsReport.csv -NoTypeInformation -Force
Write-Host "Report file(DeleteAssignmentsReport.csv) is generated at $directoryPath\DeleteAssignmentsReport.csv"
Write-Host "Updated Class details file($($directoryPathOfFile)) and generated updated file is at $($directoryPathOfFile)"