-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathActivityLog for telemetry.ps1
245 lines (186 loc) · 9.51 KB
/
ActivityLog for telemetry.ps1
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
243
244
245
# Written by Sergei Gundorov; v1 development started on 7/8/20
#
# Intent: 1. Address common friction points, usablity issues and questions related to the
# events generated by Power BI Service that are stored in the Activity Log.
# 2. Provide boiler plate code for capturing all 30 days of available data
# 3. Power BI tenant Admin scope is required to access Activity Log API.
#
# Use: log into Power BI Service with teannt Admin credentials and execute specific segment one at a time
#
# NOTE: 7/8/20 - development of code fragments started; user needs to login into service with Admin creds before using
# IMPORTANT: use Connect-PowerBIServiceAccount to connect to the service before running individual code segments
# IMPORTNAT: $day value may need to be adjusted depending on where you are located in the world relative to UTC.
# Activity log uses UTC time, so add or subtract days to accomodate for your location
# SCENARIO: sample code fragment to get limited number of attributes for specific event for specific user report viewing activity
# You need to get user's AAD object ID. You can use this Azure AD commandlet for that: https://docs.microsoft.com/en-us/powershell/module/azuread/get-azureaduser?view=azureadps-2.0
# Dates need to be in ISO 8601 format; adjust dates to span no more than 24 hours
$a=Get-powerbiActivityEvent -StartDateTime '2023-03-23T19:00:00.000' -EndDateTime '2023-03-23T23:59:59.999' -ActivityType 'ViewReport' -User [USER UPN] | ConvertFrom-Json
# you can use any attribute value to filter results further; E.g., specific event request Id is used to analyze just one specific event
$a | Select RequestId, ReportName, WorkspaceName |where {($_.RequestId -eq '[RequestId GUID of the event]')}
# SCENARIO: get the list of users for specific App
# User list can be partially (i.e., based on last 30 days of available activity) derived by combining data for 2 events: CreateApp and UpdateApp
# both events will contain OrgAppPermission property that will contain app user access list.
# Actual app installation can be tracked using InstallApp activity.
# Run each code segment separately for each event
#iterate through 30 days of activity CreateApp
$day=Get-date
for($s=0; $s -le 30; $s++)
{
$periodStart=$day.AddDays(-$s)
$base=$periodStart.ToString("yyyy-MM-dd")
write-host $base
$a=Get-powerbiActivityEvent -StartDateTime ($base+'T00:00:00.000') -EndDateTime ($base+'T23:59:59.999') -ActivityType 'CreateApp' -ResultType JsonString | ConvertFrom-Json
$c=$a.Count
for($i=0 ; $i -lt $c; $i++)
{
$r=$a[$i]
Write-Host "App Name `t: $($r.ItemName)"
` "WS Name `t: $($r.WorkSpaceName)"
` "WS ID `t`t: $($r.WorkspaceId)"
` "Created `t: $($r.CreationTime)"
` "Users `t`t: $($r.OrgAppPermission) `n"
}
}
#iterate through 30 days of activity UpdateApp
$day=Get-date
for($s=0; $s -le 30; $s++)
{
$periodStart=$day.AddDays(-$s)
$base=$periodStart.ToString("yyyy-MM-dd")
write-host $base
$a=Get-powerbiActivityEvent -StartDateTime ($base+'T00:00:00.000') -EndDateTime ($base+'T23:59:59.999') -ActivityType 'UpdateApp' -ResultType JsonString | ConvertFrom-Json
$c=$a.Count
for($i=0 ; $i -lt $c; $i++)
{
$r=$a[$i]
Write-Host "App Name `t: $($r.ItemName)"
` "WS Name `t: $($r.WorkSpaceName)"
` "WS ID `t`t: $($r.WorkspaceId)"
` "Updated `t: $($r.CreationTime)"
` "Users `t`t: $($r.OrgAppPermission) `n"
}
}
#iterate through 30 days of activity InstallApp
$day=Get-date
for($s=0; $s -le 30; $s++)
{
$periodStart=$day.AddDays(-$s)
$base=$periodStart.ToString("yyyy-MM-dd")
write-host $base
$a=Get-powerbiActivityEvent -StartDateTime ($base+'T00:00:00.000') -EndDateTime ($base+'T23:59:59.999') -ActivityType 'InstallApp' -ResultType JsonString | ConvertFrom-Json
$c=$a.Count
for($i=0 ; $i -lt $c; $i++)
{
$r=$a[$i]
Write-Host "App Name `t: $($r.ItemName)"
` "Installed `t: $($r.CreationTime)"
` "User `t`t: $($r.UserId) `n"
}
}
# SCENARIO: get the list of users for direct report sharing
# This logic and flow can be used for tracing direct dashboard sharing by substituting activity type
# Default output is formatted to return the list of users as a string. There is commented out code block to get multi-line user list
# IMPORTNAT: removal of a user or group from direct sharing access list event is not tracked
# as a consequence access list obtained using this event may be not accurate
# IMPORTANT: if the user list contains GUID instead of UPN then report was shared to a group.
# Group name and email can be obtained using Azure AD commandlets using captured ObjectId GUID
#iterate through 30 days of activity ShareReport
$day=Get-date
for($s=0; $s -le 30; $s++)
{
$periodStart=$day.AddDays(-$s)
$base=$periodStart.ToString("yyyy-MM-dd")
#write-host $base
$a=Get-powerbiActivityEvent -StartDateTime ($base+'T00:00:00.000') -EndDateTime ($base+'T23:59:59.999') -ActivityType 'ShareReport' -ResultType JsonString | ConvertFrom-Json
$c=$a.Count
for($i=0 ; $i -lt $c; $i++)
{
$r=$a[$i]
Write-Host "Rpt Name `t: $($r.ItemName)"
` "Rpt Id `t: $($r.ArtifactId)"
` "WS Name `t: $($r.WorkSpaceName)"
` "WS ID `t`t: $($r.WorkspaceId)"
` "Capacity `t: $($r.CapacityId)"
` "SharedOn `t: $($r.CreationTime.Replace('T',' ').Replace('Z',''))"
` "User `t`t: $($r.UserId)"
# NOTE: $_.RecipientEmail + $_.RecipientName or +$_.ObjectId is the case for group sharing
# can never happen both at the same time in the same JSON record
` "Shared with`t: $(($r.SharingInformation)| % {$_.RecipientEmail + $_.ObjectId +'[' + $_.ResharePermission +']'})"
#OPTIONAL: formatted output for SharingInformation attribute
#$sc= $r.SharingInformation.Count
#Write-Host "Shared with`t:"
#for($j=0;$j -lt $sc;$j++)
#{
# Write-Host "`t`t`t $($r.SharingInformation[$j].RecipientEmail)" -NoNewline
# Write-Host $r.SharingInformation[$j].ObjectId -NoNewline
# Write-Host ('[' + $r.SharingInformation[$j].ResharePermission +']')
#}
Write-host ""
}
}
# SCENARIO: 1. Address common Power BI tenant Admin/InfoSec asks to help identify active B2B users
# 2. Identify artifacts that active B2B users have access to
# This code fragment can also be used to identify all active users by tweaking home tenat domain filter
# If tenant has multiple domains for home tenant users filter can be easily expanded
# Some activity log events use user's AAD object Id instead of UPN
# App's service principal UserId property captured in the event can be used to return accesible artifacts
#Building array of all active in the last 30 days on the tenant identities
$u=@() #creating empty array to add new users
$day=Get-date
for($s=0; $s -le 30; $s++)
{
$periodStart=$day.AddDays(-$s)
$base=$periodStart.ToString("yyyy-MM-dd")
#write-host $base
$a=Get-PowerBIActivityEvent -StartDateTime ($base+'T00:00:00.000') -EndDateTime ($base+'T23:59:59.999') -ResultType JsonString | ConvertFrom-Json
$c=$a.Count
for($i=0 ; $i -lt $c; $i++)
{
$r=$a[$i]
#Write-Host "User `t`t: $($r.UserId)"
If ($u -notcontains $($r.UserId))
{
$u += $($r.UserId)
}
}
}
#slimming down active user list to only UPNs that come from external domains
#removing System, Unknown and app GUIDs
$userList = $u | where { `
$_ -notlike '*domain.com' -and ` #filter for home tenant domain
$_ -ne 'System' -and ` #removing System triggered events
$_ -ne 'Unknown' -and ` #removing events with user id as Unknown
$_ -notlike '*-????-????-????-*'` #removing app guids
}| Sort-Object
#iterating through the UPN list and idetifying accessible artifacts
foreach ($i in $userList)
{
$response=Invoke-PowerBIRESTMethod -Url ("admin/users/"+$i+"/artifactAccess") -Method Get | Convertfrom-json
$r=@() #creating empty array to add all paged responses
while($response.ContinuationToken -ne $null)
{
#Building interim results array if present
if($response.ArtifactAccessEntities.Length -ne 0)
{
$r+=$response.ArtifactAccessEntities
}
#Make another call to the API with continuation token
$response = Invoke-PowerBIRESTMethod -Url $response.continuationUri -Method Get | Convertfrom-json
}
#Capture final execution result
if($response.ArtifactAccessEntities.Length -ne 0)
{
$r+=$response.ArtifactAccessEntities
}
#Displaying summary for identity
write-host ("`nUser: " + $i)
$t=$r | Select artifactType -Unique
foreach($o in $t.artifactType)
{
$items= $r | where {$_.artifactType -eq $o}
write-host (([string]$o).PadRight(15) +":" + ([string]$items.artifactId.Count).PadLeft(4))
}
$r | Format-Table
#TODO: artifactAccess API is throttled; consider adding appropriate thread sleep/execution delay
}