Skip to main content

Find-EFSEncryptedFiles

This week, while delivering Microsoft course 20411D: Administering Windows Server 2012, I came up with an idea while delivering the chapter on encryption and advanced auditing.  One of the issues that we as Network Administrators needs to be aware of when removing a user account or formatting a client is the use of EFS.  When a user first encrypts a file or folder using EFS, a certificate is generated for EFS encryption.  If you lose this certificate, the data is no longer accessible.  One of my best practices has been to check for this certificate on the client prior to removing the account.  That way you know to start hunting for those encrypted files.
This cmdlet is going to help me modify this practice.  I’m going to do some more development with this code to also issue decrypt the files and folders.
This command was tested using the built in Domain Administrator account.  This account is the Disaster Recovery Key for files that were EFS encrypted with a domain user account.  Using this account allows you to still access these files.  It is not a good practice to leave this account enabled for use.  You may want to consider two alternative options.
1) Utilize a Data Recovery Agent
2) Disable EFS encryption in your environment.
For now, here is the Find-EFSEncryptedFiles cmdlet that I came up with last week
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
199
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
Function Find-EFSEncryptedFiles
{
<#
.SYNOPSIS
Returns files and folders on clients that have the "encrypted" attribute set
on NTFS formatted partitions.

.DESCRIPTION
Scans a remote clients hard drive for all EFS encrypted files.  This is only able
to search for files and folders on NTFS partitioned hard drives where the
Encrypted attribute is set.

.PARAMETER ComputerName

The name of the client to scan.

.PARAMETER SearchRoot

The default searchroot is the root of each fixed hard drive.  To accelerate the
search, provide the path to a specific users storage location on the
hard drive.

.PARAMETER ErrorLogPath

The location of where to store the names of all clients that could not be
contacted.

.EXAMPLE
"Client1", "Client2", "Client3" | Find-EFSEncryptedFiles

Scans three different clients for EFS encrypted Files.

.EXAMPLE
"Client1", "Client2", "Client3" | Find-EFSEncryptedFiles -SearchRoot "C:\Users\jyoder\Documents\Restricted2"

Scans three different clients for EFS encrypted files, but with the root of the search at
"C:\Users\jyoder\Documents\Restricted2".  This will accelerate the search.

.EXAMPLE
"Client1", "Client2", "Client3" | Find-EFSEncryptedFiles -Verbose  -ErrorLogPath H:\temp\EncryptErrors.txt

This example provides the same results as Example 1, but also contains Verbose output and
an error log of all clients that could not be reached.

.EXAMPLE
Get-Content -Path C:\Temp\EncryptErrors.txt | Find-EFSEncryptedFiles

This example reads an error log from a previous run of this command.  The error log
contains the names of the clients that could not be contacted the
first time the command was run.

.NOTES
===============================================================================
== Cmdlet: Find-EFSEncryptedFiles                                            ==
== Author: Jason A. Yoder                                                    ==
== Company: MCTExpert of Arizona                                             ==
== Date: June 14, 2014                                                       ==
== Copyright: All rights reserved.                                           ==
== Version: 1.0.0.0                                                          ==
== Legal: The user assumes all responsibility and liability for the usage of ==
== this PowerShell code.  MCTExpert of Arizona, Its officers, shareholders,  ==
== owners, and their relatives are not liable for any damages.  As with all  ==
== code, review it and understand it prior to usage.  It is recommended that ==
== this code be fully tested and validated in a test environment prior to    ==
== usage in a production environment.                                        ==
==                                                                           ==
== Does this code make changes: NO                                           ==
===============================================================================
#>
[cmdletbinding()]
Param(
    [Parameter(Mandatory=$True,
            ValueFromPipeline=$true)]
    [String[]]$ComputerName,
    [String]$SearchRoot = "",
    [String]$ErrorLogPath
)

    BEGIN
    {
        # If the user requests an Error Log to be generated, first verify
        # the path for the file by creating it (or erasing it) and set it
        # equal to $null.
        #
        # If the path is not valid, then display that the path is invalid.
        # Terminate the command if the path is invalid.
        If ($ErrorLogPath -ne "")
        {
           
            $ErrorLogAvailable = $False
            Try
            {
                $null | Out-File -FilePath $ErrorLogPath -ErrorAction Stop
                $ErrorLogAvailable = $True
            }
            Catch
            {
                $ColorHash = @{'ForegroundColor' = 'DarkRed'
                               'BackgroundColor' = 'Gray'}
                Write-Host "Error: Log Path is invalid              " @ColorHash
                Write-Host "Find-EFSEncryptedFiles has terminated.  " @ColorHash
                Write-Host "Correct the Errorlog path.  Refer to    " @ColorHash
                Write-Host "Get-Help Find-EFSEncryptedFile -Examples" @ColorHash
                Write-Host "for more information.                   " @ColorHash
                BREAK
            }
        }
    } # END: BEGIN BLOCK
    PROCESS{
       
        # Work on each client, one at a time.
        ForEach ($C in $Computername)
        {
            Write-Verbose "Connecting to $C"
            Try
            {
                # Initiate the remote session to the client.
                $S = New-PSSession -ComputerName $C -ErrorAction Stop
               
                Invoke-Command -ScriptBlock {
                Param($SearchRoot, $IncludeNetworkDrives)

                    # Collect the drive information
                        $Q = "Select DeviceID FROM win32_LogicalDisk WHERE DriveType = 3"
                        $Drives = Get-CimInstance -Query $Q

                    # This IF..ELSE block is used to determine the root of the search.
                    If ($SearchRoot -ne "")
                    {
                        If (Test-Path -Path $SearchRoot)
                        {
                            ForEach ($D in $Drives)
                            {
                                Get-ChildItem -Path $SearchRoot -Recurse -Attributes E |
                                Select-Object -Property FullName, Name
                            }
                        }
                        Else
                        {
                            $ColorHash = @{'ForegroundColor' =  'DarkRed'
                                           'BackgroundColor' =  'Yellow'}
                            Write-Host "The SearchRoot $SearchRoot does not exists on $(Hostname)" @ColorHash
                        }
                    }
                    Else
                    {
                      
                        ForEach ($D in $Drives)
                        {
                            Get-ChildItem -Path "$($D.DeviceID)\" -Recurse -Attributes E |
                            Select-Object -Property FullName, Name
                        }
                    }
                    } -Session $S -ArgumentList $SearchRoot, $IncludeNetworkDrives |
                Select-Object -Property Name, FullName, PSComputerName

                $S | Remove-PSSession
            } # END: TRY BLOCK
            Catch
            {
                Write-Host "Computer $C is offline" -ForegroundColor Red -BackgroundColor DarkRed
                If (($ErrorLogPath -ne "") -and ($ErrorLogAvailable -eq $true))
                {
                    $C | Out-File -FilePath $ErrorLogPath -Append
                }
            }
        } # END: ForEach ($C in $Computername)   
    } #End: PROCESS BLOCK
    END{}

} # END: Function Find-EFSEncryptedFiles

Comments

Popular posts from this blog

Adding a Comment to a GPO with PowerShell

As I'm writing this article, I'm also writing a customization for a PowerShell course I'm teaching next week in Phoenix.  This customization deals with Group Policy and PowerShell.  For those of you who attend my classes may already know this, but I sit their and try to ask the questions to myself that others may ask as I present the material.  I finished up my customization a few hours ago and then I realized that I did not add in how to put a comment on a GPO.  This is a feature that many Group Policy Administrators may not be aware of. This past summer I attended a presentation at TechEd on Group Policy.  One organization in the crowd had over 5,000 Group Policies.  In an environment like that, the comment section can be priceless.  I always like to write in the comment section why I created the policy so I know its purpose next week after I've completed 50 other tasks and can't remember what I did 5 minutes ago. In the Group Policy module for PowerShell V3, th

Return duplicate values from a collection with PowerShell

If you have a collection of objects and you want to remove any duplicate items, it is fairly simple. # Create a collection with duplicate values $Set1 = 1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 2   # Remove the duplicate values. $Set1 | Select-Object -Unique 1 2 3 4 5 6 7 What if you want only the duplicate values and nothing else? # Create a collection with duplicate values $Set1 = 1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 2   #Create a second collection with duplicate values removed. $Set2 = $Set1 | Select-Object -Unique   # Return only the duplicate values. ( Compare-Object -ReferenceObject $Set2 -DifferenceObject $Set1 ) . InputObject | Select-Object – Unique 1 2 This works with objects as well as numbers.  The first command creates a collection with 2 duplicates of both 1 and 2.   The second command creates another collection with the duplicates filtered out.  The Compare-Object cmdlet will first find items that are diffe

How to list all the AD LDS instances on a server

AD LDS allows you to provide directory services to applications that are free of the confines of Active Directory.  To list all the AD LDS instances on a server, follow this procedure: Log into the server in question Open a command prompt. Type dsdbutil and press Enter Type List Instances and press Enter . You will receive a list of the instance name, both the LDAP and SSL port numbers, the location of the database, and its status.