Turns out, I misunderstood the initial forum post.  Their goal wasn't to make sure all the team project groups (Contributors, Readers, etc.) contained only groups, but instead to make sure that all the permissions assigned to a team project were assigned to groups and not to individual users.

The silver lining is that gives me an excuse to do another example :)

For that, we'll need to use the IAuthorizationServer.ReadAccessControlList method which will return the permissions on the team project, then we can check each of the Access Control Entries (ACE's) to make sure it's assigned to a group.

Rather than get complained at, I'll go ahead and post both C# and PS versions this time :)  As with the last C# app, feel free to remove the Console.WriteLine calls if you only want the failures and no progress info.

The C# version:

    TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(args[0]);
    ICommonStructureService css = (ICommonStructureService)tfs.GetService(typeof(ICommonStructureService));
    IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));
    IAuthorizationService auth = (IAuthorizationService)tfs.GetService(typeof(IAuthorizationService));

    foreach (ProjectInfo projectInfo in css.ListProjects())
    {
        Console.WriteLine("Checking TFS security assigned to team project {0}", projectInfo.Name);
        string objectId = PermissionNamespaces.Project + projectInfo.Uri;
        AccessControlEntry[] aces = auth.ReadAccessControlList(objectId);
        foreach (AccessControlEntry ace in aces)
        {
            Identity aceIdentity = gss.ReadIdentity(SearchFactor.Sid, ace.Sid, QueryMembership.None);
            Console.WriteLine("    Checking ACE identity {0}", aceIdentity.DisplayName);
            if (aceIdentity.SecurityGroup ||
                aceIdentity.Type == IdentityType.WindowsGroup ||
                aceIdentity.Type == IdentityType.ApplicationGroup)
            {
                Console.WriteLine("        ACE identity {0} is a group", aceIdentity.DisplayName);
            }
            else
            {
                Console.Error.WriteLine("*** FAILED: ACE identity {0} of team project {1} is not a group!",
                    aceIdentity.DisplayName, projectInfo.Name);
            }
        }
    }

The PowerShell version:

    param ($serverName = $(throw 'please specify a TFS server name'))

    $tfs = get-tfs $serverName

    foreach ($project in $tfs.css.ListProjects())
    {
        $objectId = [Microsoft.TeamFoundation.PermissionNamespaces]::Project + $project.Uri
        foreach ($ace in $tfs.auth.ReadAccessControlList($objectId))
        {
            $aceIdentity = $tfs.gss.ReadIdentity('Sid', $ace.Sid, 'None')
            $isGroup = $aceIdentity.SecurityGroup -or
                       $aceIdentity.Type -eq 'WindowsGroup' -or
                       $aceIdentity.Type -eq 'ApplicationGroup'
            if (-not $isGroup)
            {
                write-warning ('ACE identity {0} of team project {1} is not a group' -f
                               $aceIdentity.DisplayName, $project.Name)
            }
        }
    }

Example output:

C:\Users\jmanning\Documents\bin\tfs# check-tfsprojsec http://tkbgitvstfat01:8080
WARNING: ACE identity Charles Downer (BGIT ALT) of team project VSTS V2 Plans is not a group