Generate Viva Engage communities and members overview with MS Graph

Generate Viva Engage communities and members overview with MS Graph

The code below will allow you to generate and export all the Viva Engage communities and members in 3 CSV files.

Code

# How to Export Viva Engage Community, Ownership, and Membership Details to CSV using Microsoft Graph API and PowerShell
# This script connects to Microsoft Graph using certificate-based authentication, retrieves Viva Engage community details plus their group owners and members, and exports the information to CSV files.
# Make sure to give your app registration the necessary permissions (Community.Read.All for application permissions) and grant admin consent before running the script.
# To export owners (M365 group owners), your app will also need permission to read group directory objects (commonly Group.Read.All or Directory.Read.All).

# Make sure you have the Microsoft.Graph PowerShell SDK installed. You can install it using:
Import-Module Microsoft.Graph.Authentication
Import-Module Microsoft.Graph.Groups

# Update these variables with your app registration details
$AppId      = "AppIdHere"
$ThumbPrint = "ThumbprintHere"
$TenantId   = "TenantIdHere"  # Prefer Tenant GUID if you have it

# Authenticate to Microsoft Graph using the app registration and certificate
try {
    Connect-MgGraph -ApplicationId $AppId -CertificateThumbprint $ThumbPrint -TenantId $TenantId -NoWelcome -ErrorAction Stop
}
catch {
    throw "Failed to authenticate to Microsoft Graph. Verify the certificate thumbprint is present (CurrentUser\\My or LocalMachine\\My), not expired, and the app registration has the correct cert configured. Details: $($_.Exception.Message)"
}

# Verify we have a Graph context
if (-not (Get-MgContext)) {
    throw "Connect-MgGraph did not establish a Graph context."
}

function Get-GraphPaged {
    param([Parameter(Mandatory)] [string] $Uri)

    $results = @()
    while ($Uri) {
        $resp = Invoke-MgGraphRequest -Method GET -Uri $Uri -ErrorAction Stop
        if ($resp.value) { $results += $resp.value }
        $Uri = $resp.'@odata.nextLink'
    }
    return $results
}

# 1) Viva Engage communities
try {
    $communities = Get-GraphPaged -Uri "https://graph.microsoft.com/v1.0/employeeExperience/communities?`$select=id,displayName,groupId,privacy"
}
catch {
    throw "Failed to list Viva Engage communities. Confirm the app has Community.Read.All (application) granted with admin consent and your Engage network is in native mode. Details: $($_.Exception.Message)"
}

$communitySummaryRows = @()
$communityMemberRows  = @()
$communityOwnerRows   = @()

# For each community, get the membership type and members. Note that the membership is managed by the connected M365 group, so we check the group type to determine if it's dynamic or assigned membership. We also handle any errors that may occur when retrieving group details.
foreach ($c in $communities) {

    # 1) Determine the membership type (Dynamic or Assigned) based on the connected M365 group. If we fail to retrieve the group details, we set the membership type to 'Unknown' and continue processing the members if possible.
    $membershipType = 'Unknown'
    try {
        $group = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/groups/$($c.groupId)?`$select=id,groupTypes,membershipRule,membershipRuleProcessingState" -ErrorAction Stop
        if ($group.groupTypes -and ($group.groupTypes -contains 'DynamicMembership')) {
            $membershipType = 'Dynamic'
        }
        else {
            $membershipType = 'Assigned'
        }
    }
    catch {
        $membershipType = 'Unknown'
    }

    # 2) Owners for each community (ownership is managed by the connected M365 group)
    $owners = @()
    if ($c.groupId) {
        try {
            $owners = Get-GraphPaged -Uri "https://graph.microsoft.com/v1.0/groups/$($c.groupId)/owners?`$select=id,displayName,userPrincipalName,mail"
        }
        catch {
            $owners = @()
        }
    }

    foreach ($o in $owners) {
        $communityOwnerRows += [pscustomobject]@{
            CommunityName     = $c.displayName
            CommunityId       = $c.id
            GroupId           = $c.groupId
            Privacy           = $c.privacy
            Membership        = $membershipType
            OwnerType         = $o.'@odata.type'
            OwnerDisplayName  = $o.displayName
            OwnerUPN          = $o.userPrincipalName
            OwnerMail         = $o.mail
            OwnerId           = $o.id
        }
    }

    # 3) Members for each community (membership is managed by the connected M365 group)
    $members = @()
    if ($c.groupId) {
        $members = Get-GraphPaged -Uri "https://graph.microsoft.com/v1.0/groups/$($c.groupId)/members?`$select=id,displayName,userPrincipalName,mail"
    }

    # Add a summary row for the community, including the membership type and member count. We use @() to ensure that if $members is $null, we still get a count of 0.
    $communitySummaryRows += [pscustomobject]@{
        CommunityName = $c.displayName
        CommunityId   = $c.id
        Privacy       = $c.privacy
        GroupId       = $c.groupId
        Membership    = $membershipType
        OwnerCount    = @($owners).Count
        MemberCount   = @($members).Count
    }

    # Add a row for each member of the community, including the community details and member details. We also include the member type (user, service principal, etc.) based on the '@odata.type' property.
    foreach ($m in $members) {
        $communityMemberRows += [pscustomobject]@{
            CommunityName     = $c.displayName
            CommunityId       = $c.id
            GroupId           = $c.groupId
            Privacy           = $c.privacy
            Membership        = $membershipType
            MemberType        = $m.'@odata.type'
            MemberDisplayName = $m.displayName
            MemberUPN         = $m.userPrincipalName
            MemberMail        = $m.mail
            MemberId          = $m.id
        }
    }
}

# If no communities were returned, warn the user and skip the CSV export. This could happen if there are no Viva Engage communities in the tenant or if the app registration does not have the correct permissions.
if (@($communitySummaryRows).Count -eq 0) {
    Write-Warning "No communities were returned. No CSV files were written."
    return
}

# Console preview
$communitySummaryRows | Sort-Object CommunityName | Format-Table -AutoSize

# CSV exports
$communitySummaryRows | Sort-Object CommunityName | Export-Csv -NoTypeInformation -Encoding utf8 "C:\Temp\VivaEngageCommunities.csv"
$communityOwnerRows   | Sort-Object CommunityName, OwnerDisplayName  | Export-Csv -NoTypeInformation -Encoding utf8 "C:\Temp\VivaEngageCommunityOwners.csv"
$communityMemberRows  | Sort-Object CommunityName, MemberDisplayName | Export-Csv -NoTypeInformation -Encoding utf8 "C:\Temp\VivaEngageCommunityMembers.csv"

Write-Host "\nWrote: VivaEngageCommunities.csv" -ForegroundColor Green
Write-Host "Wrote: VivaEngageCommunityOwners.csv" -ForegroundColor Green
Write-Host "Wrote: VivaEngageCommunityMembers.csv" -ForegroundColor Green

Example outputs

VivaEngageCommunities

VivaEngageCommunityOwners

VivaEngageCommunityMembers

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *