{"id":8710,"date":"2025-04-05T03:40:40","date_gmt":"2025-04-05T03:40:40","guid":{"rendered":"https:\/\/pariswells.com\/blog\/?p=8710"},"modified":"2025-04-05T03:40:41","modified_gmt":"2025-04-05T03:40:41","slug":"how-to-query-cloudapps-to-get-a-list-of-all-usernaames-that-have-navigated-to-a-specific-category-generative-ai","status":"publish","type":"post","link":"https:\/\/pariswells.com\/blog\/research\/how-to-query-cloudapps-to-get-a-list-of-all-usernaames-that-have-navigated-to-a-specific-category-generative-ai","title":{"rendered":"How to query cloudapps to get a list of all usernaames that have navigated to a specific Category ( generative ai )"},"content":{"rendered":"\n<p><a href=\"https:\/\/pariswells.com\/blog\/research\/how-to-setup-new-app-in-entra-with-cloudapp-api-permissions\" data-type=\"post\" data-id=\"8708\">Use this to create the App for the Tenantid\\clientid and ClientSecret<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"># Define variables with your values\n$tenantId = \"xxxx\"\n$clientId = \"xxxxx\"\n$clientSecret = \"xxxxxx\"\n$outputFile = \"C:\\Temp\\AllGenerativeAIUsage.csv\"\n\n# Function to get an access token\nfunction Get-AccessToken {\n    param (\n        [string]$TenantId,\n        [string]$ClientId,\n        [string]$ClientSecret\n    )\n    $tokenUrl = \"https:\/\/login.microsoftonline.com\/$TenantId\/oauth2\/v2.0\/token\"\n    $body = @{\n        grant_type    = \"client_credentials\"\n        client_id     = $ClientId\n        client_secret = $ClientSecret\n        scope         = \"https:\/\/graph.microsoft.com\/.default\"\n    }\n    try {\n        $tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $body\n        Write-Host \"Access Token obtained successfully.\"\n        return $tokenResponse.access_token\n    } catch {\n        Write-Error \"Failed to obtain access token: $_\"\n        exit\n    }\n}\n\n# Get the access token\n$accessToken = Get-AccessToken -TenantId $tenantId -ClientId $clientId -ClientSecret $clientSecret\n$headers = @{\n    \"Authorization\" = \"Bearer $accessToken\"\n    \"Content-Type\"  = \"application\/json\"\n}\n\n# Get all streams\n$streamsUrl = \"https:\/\/graph.microsoft.com\/beta\/security\/dataDiscovery\/cloudAppDiscovery\/uploadedStreams\"\ntry {\n    $streamsResponse = Invoke-RestMethod -Uri $streamsUrl -Headers $headers -Method Get\n    Write-Host \"Streams found: $($streamsResponse.value.Count)\"\n} catch {\n    Write-Error \"Failed to retrieve streams: $_\"\n    exit\n}\n\n# Initialize an array to store results\n$results = @()\n\n# Loop through each stream to find Generative AI apps\nforeach ($stream in $streamsResponse.value) {\n    $streamId = $stream.id\n    $streamName = $stream.displayName\n    Write-Host \"Checking stream: $streamName (ID: $streamId)\"\n\n    # Get all discovered apps for the stream (last 90 days) with pagination\n    $appsUrl = \"https:\/\/graph.microsoft.com\/beta\/security\/dataDiscovery\/cloudAppDiscovery\/uploadedStreams\/$streamId\/aggregatedAppsDetails(period=duration'P90D')\"\n    $allApps = @()\n    $nextLink = $appsUrl\n    while ($nextLink) {\n        try {\n            $response = Invoke-RestMethod -Uri $nextLink -Headers $headers -Method Get\n            $allApps += $response.value\n            $nextLink = $response.'@odata.nextLink'\n            Write-Host \"Fetched $($allApps.Count) apps so far for stream ${streamName}...\"\n        } catch {\n            Write-Warning \"Failed to retrieve apps for stream ${streamName}: $_\"\n            $nextLink = $null\n        }\n    }\n    Write-Host \"Total apps found in stream ${streamName}: $($allApps.Count)\"\n\n    # Filter for Generative AI category or ChatGPT\/OpenAI by name\/domain\n    foreach ($app in $allApps) {\n        $appId = $app.id\n        $appName = $app.displayName\n        $appCategory = $app.category\n        $appDomain = $app.domain\n        if ($appCategory -ieq \"generativeAi\" -or $appName -match \"ChatGPT\" -or $appName -match \"OpenAI\" -or $appDomain -match \"openai.com\") {\n            Write-Host \"Generative AI app found in stream ${streamName}: $appName (ID: $appId, Category: $appCategory, Domain: $appDomain)\"\n            $usersUrl = \"$appsUrl\/$appId\/users\"\n            try {\n                $usersResponse = Invoke-RestMethod -Uri $usersUrl -Headers $headers -Method Get\n                Write-Host \"Users found for ${appName}: $($usersResponse.value.Count)\"\n                if ($usersResponse.value.Count -gt 0) {\n                    Write-Host \"Usernames: $($usersResponse.value.userIdentifier -join ', ')\"\n                }\n                foreach ($user in $usersResponse.value) {\n                    $results += [PSCustomObject]@{\n                        Stream        = $streamName\n                        AppName       = $appName\n                        Username      = $user.userIdentifier\n                        ActivityCount = $user.activityCount\n                    }\n                }\n            } catch {\n                Write-Warning \"Failed to retrieve users for $appName in stream ${streamName}: $_\"\n            }\n        }\n    }\n}\n\n# Export results to CSV\nif ($results.Count -gt 0) {\n    $results | Export-Csv -Path $outputFile -NoTypeInformation\n    Write-Host \"Generative AI usage exported to $outputFile with $($results.Count) entries.\"\n} else {\n    Write-Host \"No Generative AI usage data found to export across all streams.\"\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Use this to create the App for the Tenantid\\clientid and ClientSecret<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-8710","post","type-post","status-publish","format-standard","hentry","category-research"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8710","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/comments?post=8710"}],"version-history":[{"count":1,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8710\/revisions"}],"predecessor-version":[{"id":8711,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8710\/revisions\/8711"}],"wp:attachment":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/media?parent=8710"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/categories?post=8710"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/tags?post=8710"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}