Get-Acl: Show Users Who Are Member Of (Nested) Groups, #PowerShell

From time to time, customers charge me to report about file access rights from the user account perspective, meaning a summary regarding the allowed and the denied file system accesses per user. Typically, administrators implement role-based access control (RBAC) using nested groups. Nested groups simplify the management of file system access and security audits. Individual user account only acquire access through group memberships that correspond with their business role (see also AGDLP). So much for theory! Over time, more and more exceptions prove the rule, and user accounts acquire access to file system ressources out of the RBAC concept. A few lines of PowerShell can help to distinguish between the good and bad apples.

The function below, Get-ResolvedAcl, leverages the ActiveDirectory module’s Cmdlets Get-Acl (to list explicit allow/deny access), Get-ADObject (to identify the objectClass of an Access Control Entry), and Get-ADGroupMember (to list the members of a group). Furthermore, a sub function called Get-ADNestedGroupMember calls Get-ADGroupMember recursively in order to identify user accounts in nested groups.

Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.

The login script template provided in this article maps network drives and printers based on the user’s membership in so-called “map groups”. Since a map group delivers the information which resource should be mapped for its members, you hardly ever need to modify this script due to changes in the drive or printer mappings.

For each drive and printer mapping you need to create an according AD group that follows an naming convention. By default, a map group for a network drive begins with “MAP-DRV-“, and the prefix for a network printer map group is “MAP-PRN-“. (Both prefixes can be customized by changing the constants MAP_DRIVE_GROUP_PREFIX and MAP_PRINTER_GROUP_PREFIX in this script.)

In addition to follow the naming convention you have to specify the network resources that should be mapped in the description field of a map group. In case of the map group for a network drive you must specify the drive letter followed by the unc path (seperated by a space character). In case of the printer map group you must specify the network printer’s unc path.

Since the script recognized indirect or nested group memberships you are able to add users as well as groups to the map groups. The LoadGroups function contains slightly modified code that I found on Richard L. Mueller’s website (http://www.rlmueller.net), Thanks.

Active Directory Login Script (VBScript)

Visual Basic

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

44

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

119

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

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

'

' =================================================================

'

' Script Information

' ------------------

'

' Filename: login.vbs

' Description: Active Directory Login Script

' Created: 9 Aug 2006

' Author: Frank-Peter Schultze

'

' =================================================================

'

'

'-- Need to declare variables

'

OptionExplicit

'

'-- Define MAP group prefixes

'

ConstMAP_DRIVE_GROUP_PREFIX="MAP-DRV-"

ConstMAP_PRINTER_GROUP_PREFIX="MAP-PRN-"

'

'-- Global variables

'

DimblnSilent

DimstrLastErrSource,strLastErrNumber,strLastErrDescription

DimobjNet,objUser

DimstrLoginName,strUserDomain,strUserDN

DimobjMemberOf,arrMemberOf,intGroupCount

DimstrGroup,strGroupDN,objGroup

DimstrNetUse,arrNetUse

DimstrDrv,strUnc

DimstrPrinterPath

Dimi,j,l,u

'

'-- Silent operation/do not show errors?

'

blnSilent=True

'

'-- Bind the user object . . .

'

SetobjNet=WScript.CreateObject("Wscript.Network")

strLoginName=UCase(objNet.UserName)

strUserDomain=objNet.UserDomain

strUserDN=GetDN(strUserDomain&"\" & strLoginName)

Set objUser = GetObject("LDAP://" & strUserDN)

'

'-- Load the user's direct and indirect group memberships . . .

'

Set objMemberOf = WScript.CreateObject("Scripting.Dictionary")

LoadGroups strLoginName, objUser

arrMemberOf = objMemberOf.Keys

intGroupCount = objMemberOf.Count

'

'-- Map the user's network drives . . .

'

For i = 0 To intGroupCount - 1

strGroup = Replace(arrMemberOf(i), strLoginName & "\","")

j=Len(MAP_DRIVE_GROUP_PREFIX)

If(Left(strGroup,j)=MAP_DRIVE_GROUP_PREFIX)Then

strGroupDN=GetDN(strUserDomain&"\" & strGroup)

Set objGroup = GetObject("LDAP://" & strGroupDN)

On Error Resume Next

strNetUse = Trim(CStr(objGroup.Description))

If (strNetUse <> "") Then

arrNetUse = Split(strNetUse, "")

l = LBound(arrNetUse)

u = UBound(arrNetUse)

If (l <> u) Then

strDrv = arrNetUse(l)

strUnc = arrNetUse(u)

If Not MapDrive(strDrv, strUnc) Then

TimeBombedErrorBox "MapDrive(" & Chr(34) & strDrv & Chr(34) & "," _

& Chr(34) & strUnc & Chr(34) & ")returned an error!"

End If

End If

End If

On Error GoTo 0

Set objGroup = Nothing

End If

Next

'

'-- Map the user's network printers . . .

'

For i = 0 To intGroupCount - 1

strGroup = Replace(arrMemberOf(i), strLoginName & "\","")

j=Len(MAP_PRINTER_GROUP_PREFIX)

If(Left(strGroup,j)=MAP_PRINTER_GROUP_PREFIX)Then

strGroupDN=GetDN(strUserDomain&"\" & strGroup)

Set objGroup = GetObject("LDAP://" & strGroupDN)

On Error Resume Next

strPrinterPath = CStr(objGroup.Description)

If (strPrinterPath <> "") Then

objNet.AddWindowsPrinterConnection strPrinterPath

If (Err.Number <> 0) Then

strLastErrSource = CStr(Err.Source)

strLastErrNumber = CStr(Err.Number)

strLastErrDescription = CStr(Err.Description)

Err.Clear

TimeBombedErrorBox "AddWindowsPrinterConnection" & Chr(34) _

& strPrinterPath & Chr(34) & "returned an error!"

End If

End If

On Error GoTo 0

Set objGroup = Nothing

End If

Next

Set objMemberOf = Nothing

Set objUser = Nothing

Set objNet = Nothing

'//

'// Procedures

'//

Sub LoadGroups(strLoginName, objADObject)

'

' Load the user's direct and indirect group memberships

'

Dim colstrGroups

Dim objGroup

Dim j

objMemberOf.CompareMode = vbTextCompare

colstrGroups = objADObject.memberOf

If IsEmpty(colstrGroups) Then

Exit Sub

End If

If TypeName(colstrGroups) = "String" Then

Set objGroup = GetObject("LDAP://" & colstrGroups)

If Not objMemberOf.Exists(strLoginName & "\"&objGroup.sAMAccountName)Then

objMemberOf(strLoginName&"\" & objGroup.sAMAccountName) = True

LoadGroups strLoginName, objGroup

End If

Set objGroup = Nothing

Exit Sub

End If

For j = 0 To UBound(colstrGroups)

Set objGroup = GetObject("LDAP://" & colstrGroups(j))

If Not objMemberOf.Exists(strLoginName & "\"&objGroup.sAMAccountName)Then