
A colleague of mine asked me before the weekend if I knew if there was a way to list all Everyone Except External Users and Anonymous links for OneDrive for Business. He had written a PowerShell script but had some errors. I just had gotten access to the Azure OpenAI o1-preview model, and i gave it a thought, could this model help us create a usable script instead of starting on scratch? I gave the modell a description of what we wanted it to create, and had to go back and forward a couple times with telling it about errors I discovered when running the code. I also had to do some small edits my self to the script, but after a while I had a fully workable version by mostly just giving the model instructions. I even added so the script could get the links for all SharePoint sites too. I am amazed of how long AI has come, and how much time this saved me. The difference from GPT-3.5 to o1 is huge!
Before you run the script you need to create an Azure App Registration that has the Graph API rights Directory.Read.All, Files.Read.All and Sites.Read.All. Remember to create a secret also.


| # Parameters for the app registration | |
| $ClientId = "XXXXXXXXXXXX" | |
| $TenantId = "XXXXXXXXXXXX" | |
| $ClientSecret = "XXXXXXXXXXXX" | |
| # Convert the Client Secret to a SecureString | |
| $SecureClientSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force | |
| # Create a PSCredential object with the Client ID and Secure Client Secret | |
| $ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientId, $SecureClientSecret | |
| # Connect to Microsoft Graph using the Tenant ID and Client Secret Credential | |
| Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential | |
| # Initialize the output array | |
| $OutputData = @() | |
| function Get-AllDriveItems { | |
| param ( | |
| [Parameter(Mandatory)] | |
| [string]$DriveId, | |
| [Parameter()] | |
| [string]$ParentId, | |
| [Parameter()] | |
| [string]$CurrentPath = "" | |
| ) | |
| $Items = @() | |
| if ($ParentId) { | |
| Write-Host "Processing folder with ParentId: $ParentId" -ForegroundColor Cyan | |
| try { | |
| $ParentItem = Get-MgDriveItem -DriveId $DriveId -DriveItemId $ParentId -ExpandProperty 'children' -ErrorAction Stop | |
| $DriveItems = $ParentItem.Children | |
| if($DriveItems) { | |
| Write-Host "Found $($DriveItems.Count) child items in folder '$($ParentItem.Name)' (ParentId: $ParentId)" -ForegroundColor Green | |
| } else { | |
| Write-Host "No child items found in folder '$($ParentItem.Name)' (ParentId: $ParentId)" -ForegroundColor Yellow | |
| } | |
| } catch { | |
| Write-Warning "Failed to get children for item ID '$ParentId'. Error: $_" | |
| return $Items | |
| } | |
| } else { | |
| Write-Host "Processing root folder" -ForegroundColor Cyan | |
| try { | |
| $RootItem = Get-MgDriveRoot -DriveId $DriveId -ExpandProperty 'children' -ErrorAction Stop | |
| $DriveItems = $RootItem.Children | |
| if($DriveItems) { | |
| Write-Host "Found $($DriveItems.Count) items in root folder" -ForegroundColor Green | |
| } else { | |
| Write-Host "No items found in root folder" -ForegroundColor Yellow | |
| } | |
| } catch { | |
| Write-Error "Failed to get root item and its children. Error: $_" | |
| return $Items | |
| } | |
| } | |
| if (-not $DriveItems) { | |
| Write-Verbose "No items found at this level." | |
| return $Items | |
| } | |
| foreach ($Item in $DriveItems) { | |
| # Update the file path | |
| if ($CurrentPath -eq "") { | |
| $ItemPath = "/$($Item.Name)" | |
| } else { | |
| $ItemPath = "$CurrentPath/$($Item.Name)" | |
| } | |
| # Add the ItemPath property to the item | |
| $Item | Add-Member -NotePropertyName 'ItemPath' -NotePropertyValue $ItemPath | |
| $Items += $Item | |
| Write-Host "Found item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Green | |
| if ($Item.Folder) { | |
| Write-Host "Recursing into folder: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Cyan | |
| if ($Item.Id) { | |
| $Items += Get-AllDriveItems -DriveId $DriveId -ParentId $Item.Id -CurrentPath $ItemPath | |
| } else { | |
| Write-Warning "Item '$($Item.Name)' has a null ID and cannot be processed." | |
| } | |
| } | |
| } | |
| return $Items | |
| } | |
| # Get all users except the specified one | |
| $excludedUser = "[email protected]" | |
| # Retrieve all users except the excluded one | |
| $users = Get-MgBetaUser -All -Filter "accountEnabled eq true" | Where-Object{$_.usertype -eq "Member"} | Where-Object { $_.UserPrincipalName -ne $excludedUser } | |
| foreach ($user in $users) { | |
| Write-Host "Processing user: $($user.DisplayName) <$($user.UserPrincipalName)>" -ForegroundColor Magenta | |
| try { | |
| # Get the user's default drive (OneDrive) | |
| $drive = Get-MgUserDrive -UserId $user.Id -ErrorAction Stop | |
| if ($drive) { | |
| $driveId = $drive.Id | |
| # Get all items in the user's OneDrive starting from the root | |
| $AllItems = Get-AllDriveItems -DriveId $driveId | |
| Write-Host "Total items retrieved for user $($user.DisplayName): $($AllItems.Count)" -ForegroundColor Green | |
| foreach ($Item in $AllItems) { | |
| Write-Host "Processing item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Gray | |
| # Get the permissions for the item | |
| try { | |
| $Permissions = Get-MgDriveItemPermission -DriveId $driveId -DriveItemId $Item.Id -All -ErrorAction Stop | |
| } catch { | |
| Write-Warning "Failed to get permissions for item '$($Item.Name)'. Error: $_" | |
| continue | |
| } | |
| if (!$Permissions) { | |
| Write-Host "No permissions found for item '$($Item.Name)'" -ForegroundColor Yellow | |
| } else { | |
| Write-Host "Found $($Permissions.Count) permissions for item '$($Item.Name)'" -ForegroundColor Green | |
| } | |
| foreach ($Permission in $Permissions) { | |
| # Check if the permission is a link (shared link) | |
| if ($Permission.Link) { | |
| # Initialize PermissionType variable | |
| $PermissionType = $null | |
| # Check if the link scope is "organization" or "anonymous" | |
| if ($Permission.Link.Scope -eq "organization") { | |
| $PermissionType = "Everyone except external users" | |
| } elseif ($Permission.Link.Scope -eq "anonymous") { | |
| $PermissionType = "Anonymous" | |
| } | |
| if ($PermissionType) { | |
| Write-Host "Item '$($Item.Name)' is shared with '$PermissionType'." -ForegroundColor Cyan | |
| Write-Host "URL: $($Item.WebUrl)" -ForegroundColor Blue | |
| # Create a custom object with the desired properties | |
| $OutputObject = [PSCustomObject]@{ | |
| UserPrincipalName = $user.UserPrincipalName | |
| Name = $user.DisplayName | |
| ItemName = $Item.Name | |
| ItemUrl = $Item.WebUrl | |
| FilePath = $Item.ItemPath | |
| PermissionType = $PermissionType | |
| } | |
| # Add the object to the output array | |
| $OutputData += $OutputObject | |
| } | |
| } else { | |
| # Check if permission has roles and grantedTo identifying an anonymous share | |
| if (($Permission.Roles -contains "read") -and ($Permission.GrantedToIdentities -eq $null) -and ($Permission.ShareId)) { | |
| $PermissionType = "Anonymous" | |
| Write-Host "Item '$($Item.Name)' has an anonymous share link." -ForegroundColor Cyan | |
| Write-Host "URL: $($Item.WebUrl)" -ForegroundColor Blue | |
| # Create a custom object with the desired properties | |
| $OutputObject = [PSCustomObject]@{ | |
| UserPrincipalName = $user.UserPrincipalName | |
| Name = $user.DisplayName | |
| ItemName = $Item.Name | |
| ItemUrl = $Item.WebUrl | |
| FilePath = $Item.ItemPath | |
| PermissionType = $PermissionType | |
| } | |
| # Add the object to the output array | |
| $OutputData += $OutputObject | |
| } else { | |
| Write-Host "Permission is not a link or not anonymous for item '$($Item.Name)'" -ForegroundColor Gray | |
| } | |
| } | |
| } | |
| } | |
| } else { | |
| Write-Host "User $($user.DisplayName) does not have a OneDrive provisioned." -ForegroundColor Yellow | |
| } | |
| } catch { | |
| Write-Warning "Failed to process user $($user.DisplayName). Error: $_" | |
| continue | |
| } | |
| } | |
| # Now, process all SharePoint sites and their document libraries | |
| Write-Host "Starting to process all SharePoint sites and their document libraries..." -ForegroundColor Magenta | |
| try { | |
| # Retrieve all SharePoint sites | |
| $sites = Get-MgSite -All | Where-Object { $_.WebUrl -notlike "*personal*" } | |
| } catch { | |
| Write-Error "Failed to retrieve SharePoint sites. Error: $_" | |
| exit | |
| } | |
| foreach ($site in $sites) { | |
| Write-Host "Processing site: $($site.DisplayName) <$($site.WebUrl)>" -ForegroundColor Magenta | |
| try { | |
| # Get all document libraries (drives) in the site | |
| $drives = Get-MgSiteDrive -SiteId $site.Id -ErrorAction Stop | |
| foreach ($drive in $drives) { | |
| Write-Host "Processing document library: $($drive.Name)" -ForegroundColor Cyan | |
| $driveId = $drive.Id | |
| # Get all items in the document library starting from the root | |
| $AllItems = Get-AllDriveItems -DriveId $driveId | |
| Write-Host "Total items retrieved in document library '$($drive.Name)': $($AllItems.Count)" -ForegroundColor Green | |
| foreach ($Item in $AllItems) { | |
| Write-Host "Processing item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Gray | |
| # Get the permissions for the item | |
| try { | |
| $Permissions = Get-MgDriveItemPermission -DriveId $driveId -DriveItemId $Item.Id -All -ErrorAction Stop | |
| } catch { | |
| Write-Warning "Failed to get permissions for item '$($Item.Name)'. Error: $_" | |
| continue | |
| } | |
| if (!$Permissions) { | |
| Write-Host "No permissions found for item '$($Item.Name)'" -ForegroundColor Yellow | |
| } else { | |
| Write-Host "Found $($Permissions.Count) permissions for item '$($Item.Name)'" -ForegroundColor Green | |
| } | |
| foreach ($Permission in $Permissions) { | |
| # Check if the permission is a link (shared link) | |
| if ($Permission.Link) { | |
| # Initialize PermissionType variable | |
| $PermissionType = $null | |
| # Check if the link scope is "organization" or "anonymous" | |
| if ($Permission.Link.Scope -eq "organization") { | |
| $PermissionType = "Everyone except external users" | |
| } elseif ($Permission.Link.Scope -eq "anonymous") { | |
| $PermissionType = "Anonymous" | |
| } | |
| if ($PermissionType) { | |
| Write-Host "Item '$($Item.Name)' is shared with '$PermissionType'." -ForegroundColor Cyan | |
| Write-Host "URL: $($Item.WebUrl)" -ForegroundColor Blue | |
| # Create a custom object with the desired properties | |
| $OutputObject = [PSCustomObject]@{ | |
| SiteUrl = $site.WebUrl | |
| SiteName = $site.DisplayName | |
| DocumentLibrary = $drive.Name | |
| ItemName = $Item.Name | |
| ItemUrl = $Item.WebUrl | |
| FilePath = $Item.ItemPath | |
| PermissionType = $PermissionType | |
| } | |
| # Add the object to the output array | |
| $OutputData += $OutputObject | |
| } | |
| } else { | |
| # Check if permission has roles and grantedTo identifying an anonymous share | |
| if (($Permission.Roles -contains "read") -and ($Permission.GrantedToIdentities -eq $null) -and ($Permission.ShareId)) { | |
| $PermissionType = "Anonymous" | |
| Write-Host "Item '$($Item.Name)' has an anonymous share link." -ForegroundColor Cyan | |
| Write-Host "URL: $($Item.WebUrl)" -ForegroundColor Blue | |
| # Create a custom object with the desired properties | |
| $OutputObject = [PSCustomObject]@{ | |
| SiteUrl = $site.WebUrl | |
| SiteName = $site.DisplayName | |
| DocumentLibrary = $drive.Name | |
| ItemName = $Item.Name | |
| ItemUrl = $Item.WebUrl | |
| FilePath = $Item.ItemPath | |
| PermissionType = $PermissionType | |
| } | |
| # Add the object to the output array | |
| $OutputData += $OutputObject | |
| } else { | |
| Write-Host "Permission is not a link or not anonymous for item '$($Item.Name)'" -ForegroundColor Gray | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } catch { | |
| Write-Warning "Failed to process site $($site.DisplayName). Error: $_" | |
| continue | |
| } | |
| } | |
| # Output or process the collected data as needed | |
| # For example, output to console | |
| $OutputData | |
| # Optionally, export to CSV | |
| # $OutputData | Export-Csv -Path 'C:\Path\To\Output.csv' -NoTypeInformation |
Get-AllDriveItems isn’t a PoSh cmdlet. How did you get this to work
LikeLike
If you look at line 20 its the name of a function, i just named it to look like a powershell command
LikeLike