Remove-SensitiveData.ps1

<#
.SYNOPSIS
    Remove sensitive data from object or string.
.EXAMPLE
    PS C:\>$MyString = 'My password is: "SuperSecretString"'
    PS C:\>Remove-SensitiveData ([ref]$MyString) -FilterValues "Super","String"
    This removes the word "Super" and "String" from the input string with no output.
.EXAMPLE
    PS C:\>Remove-SensitiveData 'My password is: "SuperSecretString"' -FilterValues "Super","String" -PassThru
    This removes the word "Super" and "String" from the input string and return the result.
.INPUTS
    System.Object
#>

function Remove-SensitiveData
{
    [CmdletBinding()]
    [OutputType([object])]
    param (
        # Object from which to remove sensitive data.
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [object] $InputObjects,
        # Sensitive string values to remove from input object.
        [Parameter(Mandatory=$true)]
        [AllowNull()]
        [AllowEmptyString()]
        [string[]] $FilterValues,
        # Replacement value for senstive data.
        [Parameter(Mandatory=$false)]
        [string] $ReplacementValue = '********',
        # Copy the input object rather than remove data directly from input.
        [Parameter(Mandatory=$false)]
        [switch] $Clone,
        # Output object with sensitive data removed.
        [Parameter(Mandatory=$false)]
        [switch] $PassThru
    )

    process {
        if ($InputObjects.GetType().FullName.StartsWith('System.Management.Automation.PSReference')) {
            if ($Clone) { $OutputObjects = $InputObjects.Value.Clone() }
            else { $OutputObjects = $InputObjects }
        }
        else {
            if ($Clone) { $OutputObjects = [ref]$InputObjects.Clone() }
            else {
                if ($InputObjects -is [System.ValueType] -or $InputObjects -is [string]) { Write-Warning ('The input of type [{0}] was not passed by reference. Senstive data will not be removed from the original input.' -f $InputObjects.GetType()) }
                $OutputObjects = [ref]$InputObjects
            }
        }

        if ($OutputObjects.Value -is [string]) {
            foreach ($FilterValue in $FilterValues) {
                if ($OutputObjects.Value -and $FilterValue) { $OutputObjects.Value = $OutputObjects.Value.Replace($FilterValue,$ReplacementValue) }
            }
        }
        elseif ($OutputObjects.Value -is [array] -or $OutputObjects.Value -is [System.Collections.ArrayList] -or $OutputObjects.Value.GetType().FullName.StartsWith('System.Collections.Generic.List')) {
            for ($ii = 0; $ii -lt $OutputObjects.Value.Count; $ii++) {
                if ($null -ne $OutputObjects.Value[$ii] -and $OutputObjects.Value[$ii] -isnot [ValueType]) {
                    $OutputObjects.Value[$ii] = Remove-SensitiveData ([ref]$OutputObjects.Value[$ii]) -FilterValues $FilterValues -PassThru
                }
            }
        }
        elseif ($OutputObjects.Value -is [hashtable] -or $OutputObjects.Value -is [System.Collections.Specialized.OrderedDictionary] -or $OutputObjects.Value.GetType().FullName.StartsWith('System.Collections.Generic.Dictionary')) {
            [array] $KeyNames = $OutputObjects.Value.Keys
            for ($ii = 0; $ii -lt $KeyNames.Count; $ii++) {
                if ($null -ne $OutputObjects.Value[$KeyNames[$ii]] -and $OutputObjects.Value[$KeyNames[$ii]] -isnot [ValueType]) {
                    $OutputObjects.Value[$KeyNames[$ii]] = Remove-SensitiveData ([ref]$OutputObjects.Value[$KeyNames[$ii]]) -FilterValues $FilterValues -PassThru
                }
            }
        }
        elseif ($OutputObjects.Value -is [object] -and $OutputObjects.Value -isnot [ValueType])
        {
            [array] $PropertyNames = $OutputObjects.Value | Get-Member -MemberType Property,NoteProperty
            for ($ii = 0; $ii -lt $PropertyNames.Count; $ii++) {
                $PropertyName = $PropertyNames[$ii].Name
                if ($null -ne $OutputObjects.Value.$PropertyName -and $OutputObjects.Value.$PropertyName -isnot [ValueType]) {
                    $OutputObjects.Value.$PropertyName = Remove-SensitiveData ([ref]$OutputObjects.Value.$PropertyName) -FilterValues $FilterValues -PassThru
                }
            }
        }
        else
        {
            ## Non-Terminating Error
            $Exception = New-Object ArgumentException -ArgumentList ('Cannot remove senstive data from input of type {0}.' -f $OutputObjects.Value.GetType())
            Write-Error -Exception $Exception -Category ([System.Management.Automation.ErrorCategory]::ParserError) -CategoryActivity $MyInvocation.MyCommand -ErrorId 'RemoveSensitiveDataFailureTypeNotSupported' -TargetObject $OutputObjects.Value
            continue
        }

        if ($PassThru -or $Clone) {
            ## Return the object with sensitive data removed.
            if ($OutputObjects.Value -is [array] -or $OutputObjects.Value -is [System.Collections.ArrayList] -or $OutputObjects.Value.GetType().FullName.StartsWith('System.Collections.Generic.List')) {
                Write-Output $OutputObjects.Value -NoEnumerate
            }
            else {
                Write-Output $OutputObjects.Value
            }
        }
    }
}