UserCertificates.psm1

#region Get-ActiveDirectoryObjectCertificate
function Get-ActiveDirectoryObjectCertificate
{
    <#
    .Synopsis
       Get the user certificates from Active Directory
    .DESCRIPTION
       Get the certificates saved on a user object in X509 format
    .EXAMPLE
       Get-ActiveDirectoryObjectCertificate -UserPrincipalName cpolydorou@lab.local
 
       Get the certificates on the cpolydorou@lab.local Active Directory object
    #>


    #region Parameters
    [CmdletBinding(DefaultParameterSetName='Parameter Set DN', 
                  PositionalBinding=$false)]
    Param
    (
        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set DN')]
        [string[]]
        $DistinguishedName,

        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set UPN')]
        [string[]]
        $UserPrincipalName,

        # The SamAccountName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set SAM')]
        [string[]]
        $SamAccountName
    )
    #endregion

    #region Begin
    Begin
    {
        #region Directory Searcher setup
        $root = New-Object System.DirectoryServices.DirectoryEntry
        $searcher = New-Object System.DirectoryServices.DirectorySearcher
        $searcher.SearchRoot = $root
        $searcher.SearchScope = "Subtree"
        $searcher.PropertiesToLoad.Add("distinguishedName") | Out-Null
        $searcher.PropertiesToLoad.Add("sAMAccountName") | Out-Null
        $searcher.PropertiesToLoad.Add("userPrincipalName") | Out-Null
        $searcher.PropertiesToLoad.Add("userCertificate") | Out-Null
        #endregion
    }
    #endregion

    #region Process
    Process
    {
        #region DistinguishedName
        if($DistinguishedName)
        {
            foreach($dn in $DistinguishedName)
            {
                $filter = "(distinguishedName=$dn)"
                $searcher.Filter = $filter

                try
                {
                    $result = $searcher.FindOne()
                }
                catch
                {
                    Write-Error "Error"

                }
                if($result -ne $null)
                {
                    __CustomObject -user $result  
                }
                else
                {
                    Write-Error "Could not find an object with DistinguishedName $dn"
                }
            }
        }
        #endregion

        #region UserPrincipalName
        if($UserPrincipalName)
        {
            foreach($upn in $UserPrincipalName)
            {
                $filter = "(UserPrincipalName=$upn)"
                $searcher.Filter = $filter
                $result = $searcher.FindOne()

                if($result -ne $null)
                {
                    __CustomObject -user $result  
                }
                else
                {
                    Write-Error "Could not find an object with UserPrincipalName $upn"
                }
            }
        }
        #endregion

        #region SamAccountName
        if($SamAccountName)
        {
            foreach($sam in $SamAccountName)
            {
                $filter = "(SamAccountName=$sam)"
                $searcher.Filter = $filter
                $result = $searcher.FindOne()
                
                if($result -ne $null)
                {
                    __CustomObject -user $result  
                }
                else
                {
                    Write-Error "Could not find an object with SamAccountName $sam"
                }
            }
        }
        #endregion
    }
    #endregion

    #region End
    End {}
    #endregion
}
#endregion

#region Remove-ActiveDirectoryObjectCertificate
function Remove-ActiveDirectoryObjectCertificate
{
    <#
    .Synopsis
       Remove a certificate from an Active Directory user object
    .DESCRIPTION
       Remove a certificate from an Active Directory user object
    .EXAMPLE
        Remove-ActiveDirectoryObjectCertificate -UserPrincipalName cpolydorou@lab.local -Thumbprint "6C1FC463805E72EBF401E039EC307627078C1339" -Verbose
         
        Removes the certificate with thumbprint 6C1FC463805E72EBF401E039EC307627078C1339 from the object.
    .EXAMPLE
        Remove-ActiveDirectoryObjectCertificate -UserPrincipalName cpolydorou@lab.local -Verbose
 
        Removes all certificates from the object
 
    .EXAMPLE
        Remove-ActiveDirectoryObjectCertificate -UserPrincipalName cpolydorou@lab.local -Expired -Verbose
 
        Removes all expired certificates from the object.
    #>


    #region Parameters
    [CmdletBinding(DefaultParameterSetName='Parameter Set DN',
                   SupportsShouldProcess=$true,  
                   ConfirmImpact='High',
                   PositionalBinding=$false)]
    Param
    (
        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set DN')]
        [string]
        $DistinguishedName,

        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $UserPrincipalName,

        # The SamAccountName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set SAM')]
        [string]
        $SamAccountName,

        # The thumbprint of the certificate to remove
        [Parameter(Mandatory=$false, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set SAM')]
        [Parameter(Mandatory=$false, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set DN')]
        [Parameter(Mandatory=$false, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $Thumbprint,

        # Remove the expired certificates
        [Parameter(Mandatory=$false, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set SAM')]
        [Parameter(Mandatory=$false, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set DN')]
        [Parameter(Mandatory=$false, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set UPN')]
        [switch]
        $Expired
    )
    #endregion

    #region Begin
    Begin
    {
        #region Directory Searcher setup
        $root = New-Object System.DirectoryServices.DirectoryEntry
        $searcher = New-Object System.DirectoryServices.DirectorySearcher
        $searcher.SearchRoot = $root
        $searcher.SearchScope = "Subtree"
        $searcher.PropertiesToLoad.Add("distinguishedName") | Out-Null
        $searcher.PropertiesToLoad.Add("sAMAccountName") | Out-Null
        $searcher.PropertiesToLoad.Add("userPrincipalName") | Out-Null
        $searcher.PropertiesToLoad.Add("userCertificate") | Out-Null
        #endregion
    }
    #endregion

    #region Process
    Process
    {
        #region Get the user
        Write-Verbose "Getting the Active Directory object..."
        #region DistinguishedName
        if($DistinguishedName)
        {
            $filter = "(distinguishedName=$DistinguishedName)"
            $target = $DistinguishedName
        }
        #endregion

        #region UserPrincipalName
        if($UserPrincipalName)
        {
            $filter = "(UserPrincipalName=$UserPrincipalName)"
            $target = $UserPrincipalName
        }
        #endregion

        #region SamAccountName
        if($SamAccountName)
        {
            $filter = "(SamAccountName=$SamAccountName)"
            $target = $SamAccountName
        }
        #endregion

        # Get the object
        $searcher.Filter = $filter
        $Result = $searcher.FindOne()
        $Object = [ADSI]$Result.GetDirectoryEntry()
        
        # Check the result
        if($Result -eq $null)
        {
            Write-Error "Could not find the specified object."
            return
        }

        # Get the certificates
        $Certificates = New-Object -TypeName System.Collections.ArrayList


        for($i=0; $i -lt $Object.Properties.usercertificate.Count; $i++)
        {
            $Certificates.Add($Object.usercertificate[$i]) | Out-Null
        }
        #endregion

        #region Remove the certificate
        for($i = 0; $i -lt $Certificates.Count; $i++)
        {
            # Convert the certificate to X509 format
            $cert = __ADtoX509 -ADCertificate $Certificates[$i]
            
            #region Remove using thumbprint
            if($Thumbprint)
            {
                if($Thumbprint -eq $cert.Thumbprint)
                {
                    if ($pscmdlet.ShouldProcess($target, "Remove-ActiveDirectoryUserCertificate with Thumbprint $($cert.thumbprint)"))
                    {
                        #region Update the object
                        try
                        {
                            Write-Verbose "Removing certificate with thumbprint $($cert.Thumbprint)..."
                            $Object.userCertificate.Remove($Object.userCertificate[$i])

                            $Object.CommitChanges()
                            Write-Verbose "Certificate $($cert.Thumbprint) removed successfully."
                        }
                        catch
                        {
                            Write-Error ("Could not remove certificate $($cert.Thumbprint)" + "`n" + $_)
                        }
                        #endregion
                    }

                    continue
                }
            }
            #endregion

            #region Remove expired
            if($Expired)
            {
                if(([DateTime]::Now) -gt $cert.NotAfter)
                {
                    if ($pscmdlet.ShouldProcess($target, "Remove-ActiveDirectoryUserCertificate with Thumbprint $($cert.thumbprint)"))
                    {
                        #region Update the object
                        try
                        {
                            Write-Verbose "Removing certificate with thumbprint $($cert.Thumbprint)..."
                            $Object.userCertificate.Remove($Object.userCertificate[$i])

                            $Object.CommitChanges()
                            Write-Verbose "Certificate $($cert.Thumbprint) removed successfully."
                        }
                        catch
                        {
                            Write-Error ("Could not remove certificate $($cert.Thumbprint)" + "`n" + $_)
                        }
                        #endregion
                    }
                }

                continue
            }
            #endregion

            #region remove all
            if( (-not $Thumbprint) -and (-not $Expired))
            {
                if ($pscmdlet.ShouldProcess($target, "Remove-ActiveDirectoryUserCertificate with Thumbprint $($cert.thumbprint)"))
                {
                    #region Update the object
                    try
                    {
                        Write-Verbose "Removing certificate with thumbprint $($cert.Thumbprint)..."
                        $Object.userCertificate.Remove($Object.userCertificate[$i])

                        $Object.CommitChanges()
                        Write-Verbose "Certificate $($cert.Thumbprint) removed successfully."
                    }
                    catch
                    {
                        Write-Error ("Could not remove certificate $($cert.Thumbprint)" + "`n" + $_)
                    }
                    #endregion
                }
            }
            #endregion
        }
        #endregion
    }
    #endregion

    #region End
    End {}
    #endregion
}
#endregion

#region Import-ActiveDirectoryObjectCertificate
function Import-ActiveDirectoryObjectCertificate
{
    <#
    .Synopsis
       Add a certificate to Active Directory
    .DESCRIPTION
       Add a certificate to Active Directory from file
    .EXAMPLE
    Import-ActiveDirectoryObjectCertificate -UserPrincipalName cpolydorou@lab.local -Path .\certificate.cer -Verbose
 
    Import the certificate from the file "certificate.cer" to the cpolydorou@lab.local Active Directory object.
    #>


    #region Parameters
    [CmdletBinding(DefaultParameterSetName='Parameter Set DN', 
                  PositionalBinding=$false)]
    Param
    (
        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set DN')]
        [string]
        $DistinguishedName,

        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $UserPrincipalName,

        # The SamAccountName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set SAM')]
        [string]
        $SamAccountName,

        # The path to the file
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set SAM')]
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set DN')]
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $Path
    )
    #endregion

    #region Begin
    Begin
    {
        #region Directory Searcher setup
        $root = New-Object System.DirectoryServices.DirectoryEntry
        $searcher = New-Object System.DirectoryServices.DirectorySearcher
        $searcher.SearchRoot = $root
        $searcher.SearchScope = "Subtree"
        $searcher.PropertiesToLoad.Add("distinguishedName") | Out-Null
        $searcher.PropertiesToLoad.Add("sAMAccountName") | Out-Null
        $searcher.PropertiesToLoad.Add("userPrincipalName") | Out-Null
        $searcher.PropertiesToLoad.Add("userCertificate") | Out-Null
        #endregion
    }
    #endregion

    #region Process
    Process
    {
        #region Get the object
        Write-Verbose "Getting the object..."
        #region DistinguishedName
        if($DistinguishedName)
        {
            foreach($dn in $DistinguishedName)
            {
                $filter = "(distinguishedName=$dn)"
                $searcher.Filter = $filter

                if($result -eq $null)
                {
                    Write-Error "Could not find an object with DistinguishedName $dn"
                }
            }
        }
        #endregion

        #region UserPrincipalName
        if($UserPrincipalName)
        {
            foreach($upn in $UserPrincipalName)
            {
                $filter = "(UserPrincipalName=$upn)"
                $searcher.Filter = $filter
                $result = $searcher.FindOne()

                if($result -eq $null)
                {
                    Write-Error "Could not find an object with UserPrincipalName $upn"
                }
            }
        }
        #endregion

        #region SamAccountName
        if($SamAccountName)
        {
            foreach($sam in $SamAccountName)
            {
                $filter = "(SamAccountName=$sam)"
                $searcher.Filter = $filter
                $result = $searcher.FindOne()
                
                if($result -eq $null)
                {
                    Write-Error "Could not find an object with SamAccountName $sam"
                }
            }
        }
        #endregion

        $Object = [ADSI]$Result.GetDirectoryEntry()
        #endregion

        #region Import the certificate
        Write-Verbose "Reading the file..."

        if((Test-Path -Path $Path) -ne $true)
        {
            Write-Error "Failed to open the file."
            return
        }
        else
        {
            $fullPath = (Resolve-Path -Path $Path).Path
        }

        try
        {
            $newCert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2

            $newCert.Import($fullPath)
        }
        catch
        {
            Write-Error "Failed read the certificate from the file.`n$_"
            return
        }

        Write-Verbose "Adding the certificate to the Active Directory object..."
        try
        {
            $Object.userCertificate.Add($newCert.GetRawCertData()) | Out-Null
            $Object.CommitChanges()
        }
        catch
        {
            Write-Error "Failed to add the certificate to the Active Directory object."
        }

        #endregion
    }
    #endregion

    #region End
    End {}
    #endregion
}
#endregion

#region Export-ActiveDirectoryObjectCertificate
function Export-ActiveDirectoryObjectCertificate
{
    <#
    .Synopsis
       Save the user certificate from Active Directory to file
    .DESCRIPTION
       Save the user certificate from Active Directory to file
    .EXAMPLE
       Export-ActiveDirectoryObjectCertificate -UserPrincipalName cpolydorou@lab.local -Thumbprint "176ED95A217C692131DF1275B323F5574EFBF00A" -Path C:\Temp\file.cer
 
       Exports the certificate with thumbprint 176ED95A217C692131DF1275B323F5574EFBF00A to the file C:\Temp\file.cer
    #>


    #region Parameters
    [CmdletBinding(DefaultParameterSetName='Parameter Set DN', 
                  PositionalBinding=$false)]
    Param
    (
        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set DN')]
        [string]
        $DistinguishedName,

        # The DistinguishedName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $UserPrincipalName,

        # The SamAccountName of the user
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=0,
                   ParameterSetName='Parameter Set SAM')]
        [string]
        $SamAccountName,

        # The thumbprint of the certificate to remove
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set SAM')]
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set DN')]
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $Thumbprint,

        # The path to the file
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set SAM')]
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set DN')]
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false,
                   ValueFromPipelineByPropertyName=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2,
                   ParameterSetName='Parameter Set UPN')]
        [string]
        $Path
    )
    #endregion

    #region Begin
    Begin
    {
        #region Directory Searcher setup
        $root = New-Object System.DirectoryServices.DirectoryEntry
        $searcher = New-Object System.DirectoryServices.DirectorySearcher
        $searcher.SearchRoot = $root
        $searcher.SearchScope = "Subtree"
        $searcher.PropertiesToLoad.Add("distinguishedName") | Out-Null
        $searcher.PropertiesToLoad.Add("sAMAccountName") | Out-Null
        $searcher.PropertiesToLoad.Add("userPrincipalName") | Out-Null
        $searcher.PropertiesToLoad.Add("userCertificate") | Out-Null
        #endregion
    }
    #endregion

    #region Process
    Process
    {
        #region Get the certificates
        Write-Verbose "Getting the object's certificates..."
        #region DistinguishedName
        if($DistinguishedName)
        {
            $Certificates = Get-ActiveDirectoryObjectCertificate -DistinguishedName $DistinguishedName | % Certificate
        }
        #endregion

        #region UserPrincipalName
        if($UserPrincipalName)
        {
            $Certificates = Get-ActiveDirectoryObjectCertificate -UserPrincipalName $UserPrincipalName | % Certificate
        }
        #endregion

        #region SamAccountName
        if($SamAccountName)
        {
            $Certificates = Get-ActiveDirectoryObjectCertificate -SamAccountName $SamAccountName | % Certificate
        }
        #endregion
        #endregion

        #region Export the certificate
        $found = 0
        foreach($c in $Certificates)
        {
            if($c.Thumbprint -eq $Thumbprint)
            {
                Write-Verbose "Exporting certificate to file..."
                [System.IO.File]::WriteAllBytes($Path, $c.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert))
                $found++
            }
        }

        if($found -eq 0)
        {
            Write-Error "Could not find the specified certificate."
        }
        #endregion
    }
    #endregion

    #region End
    End {}
    #endregion
}
#endregion

#region Helper Functions
#region __ADtoX509
Function __ADtoX509
{
    # Convert a Base64 string to X509 certificate
    Param
    (
        [string[]]$ADCertificate
    )

    try 
    {
        $X509Cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2
        $X509Cert.Import([byte[]]$ADCertificate)

        $X509Cert
    }
    catch{}
}
#endregion

#region __CustomObject
function __CustomObject
{
    Param
    (
        $user
    )

    $cert = $user.Properties.usercertificate |
                %{
                    __ADtoX509 -ADCertificate $_
                }

    $props = [ordered]@{
                        DistinguishedName = $user.Properties.distinguishedname.Item(0)
                        UserPrincipalName = $user.Properties.userprincipalname.Item(0)
                        SamAccountName = $user.Properties.samaccountname.Item(0)
                        Certificate = $cert
                     }

    New-Object -TypeName PSObject -Property $props
}
#endregion
#endregion

#region Exports
Export-ModuleMember -Function Get-ActiveDirectoryObjectCertificate
Export-ModuleMember -Function Remove-ActiveDirectoryObjectCertificate
Export-ModuleMember -Function Import-ActiveDirectoryObjectCertificate
Export-ModuleMember -Function Export-ActiveDirectoryObjectCertificate
#endregion