functions/Set-MipLabel.ps1

function Set-MipLabel {
    <#
    .SYNOPSIS
        Applies a sensitivity label to a file.
     
    .DESCRIPTION
        Applies a sensitivity label to a file.
        _Downgrading_ a label's security level requires a Justification.
         
        The file being labeled will be temporarily duplicated, meaning ...
        - Write access to the folder is needed, not just the file (specifically, the ability to create a new file)
        - Delete access to the file being labeled is required (to enable rollback in case of error, the file gets renamed, which is essentially a delete & create operation)
        - Exclusive accesss to the file is required
        - Enough storage to copy the file is needed
 
        Must be connected first using "Connect-InformationProtection".
     
    .PARAMETER Label
        The label to apply.
     
    .PARAMETER Path
        Path to the file to label.
     
    .PARAMETER Justification
        The reason for the label change.
        This is required for any _changes_ to the label that downgrade its protection status.
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
     
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
     
    .EXAMPLE
        PS C:\> Set-MipLabel -Path .\test.docx -Label 'Highly Confidential\All Employees'
 
        Updates the label of test.docx to "Highly Confidential\All Employees"
 
    .EXAMPLE
        PS C:\> Get-ChildItem -Recurse -File | Set-MipLabel -Label Public
 
        Updates the label on all files in the current folder and subfolders to "Public"
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [PsfArgumentCompleter('InformationProtection.Label')]
        [string]
        $Label,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PsfFile]
        $Path,

        [string]
        $Justification
    )
    begin {
        Assert-MIPConnection -Cmdlet $PSCmdlet
        $killIt = $ErrorActionPreference -eq 'Stop'

        $labelObject = Get-MipLabel -Filter $Label
        if (-not $labelObject) {
            Stop-PSFFunction -String 'Set-MipLabel.Error.LabelNotFound' -StringValues $Label -Cmdlet $PSCmdlet -EnableException $true -Category InvalidArgument
        }
        if ($labelObject.ID -contains $Label) { $labelObject = $labelObject | Where-Object ID -eq $Label }
        elseif ($labelObject.FQLA -contains $Label) { $labelObject = $labelObject | Where-Object FQLA -eq $Label }

        if ($labelObject.Count -gt 1) {
            Stop-PSFFunction -String 'Set-MipLabel.Error.LabelAmbiguous' -StringValues $Label, ($labelObject.FQLA -join ', ') -Cmdlet $PSCmdlet -EnableException $true -Category InvalidArgument
        }
    }
    process {
        foreach ($filePath in $Path) {
            $file = [InformationProtection.File]$filePath
            $directory = Split-Path -Path $file.Path
            $fileName = Split-Path -Path $file.Path -Leaf
            $tempNewPath = Join-Path -Path $directory -ChildPath ([Guid]::NewGuid())
            $tempOldName = [Guid]::NewGuid().ToString()
            $tempOldPath = Join-Path -Path $directory -ChildPath $tempOldName
            Invoke-PSFProtectedCommand -ActionString 'Set-MipLabel.ApplyLabel' -ActionStringValues $labelObject.Name, $labelObject.ID -Target $file.Path -ScriptBlock {
                # Step 1: Label & New File
                $file.SetLabel($labelObject.ID, $tempNewPath, $Justification)

                # Step 2: Rename old file to temp name
                try { Rename-Item -LiteralPath $file.Path -NewName $tempOldName -Force -ErrorAction Stop }
                catch {
                    Remove-Item -LiteralPath $tempNewPath -Force
                    throw
                }

                # Step 3: Rename labeled file to original name
                try { Rename-Item -LiteralPath $tempNewPath -NewName $fileName -Force -ErrorAction Stop }
                catch {
                    # Rollback and delete new file
                    Rename-Item -LiteralPath $tempOldPath -NewName $fileName -Force
                    Remove-Item -LiteralPath $tempNewPath -Force
                    throw
                }

                # Step 4: Delete Renamed unlabeled file
                Remove-Item -LiteralPath $tempOldPath
            } -EnableException $killIt -PSCmdlet $PSCmdlet -Continue
        }
    }
}