Tuesday, April 8, 2014

Enumerating TFS Permissions for items within the TPC for audit and logging

Below is a powershell cmdlet that I threw together to enumerate data out of our TFS instance.  This is used to keep track of permissions in the event we need to re-create them and to allow us to audit it to make sure things don't change.

This can also be used to keep a pre-production environment's permissions in sync with Production.

I'm not the best at Powershell but it works :)

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

function GetGroupMembership {
 [CmdletBinding()]
 [OutputType([System.Data.Datatable])]
 PARAM (
  [Parameter(Mandatory=$true, Position = 0)]
  [Microsoft.TeamFoundation.Framework.Client.TeamFoundationIdentity]
  $TFIdentity
 )
 
 $tabName = $TFIdentity.DisplayName + "Membership"
 $table = New-Object system.Data.DataTable “$tabName”
 $col1 = New-Object system.Data.DataColumn GroupName,([string])
 $col2 = New-Object system.Data.DataColumn DisplayName,([string])
 $col3 = New-Object system.Data.DataColumn UniqueName,([string])

 #Add the Columns
 $table.columns.add($col1)
 $table.columns.add($col2)
 $table.columns.add($col3)
  
 #Membership
 $GroupIdentity = $ims.ReadIdentity($TFIdentity.Descriptor,[Microsoft.TeamFoundation.Framework.Common.MembershipQuery]::Direct,[Microsoft.TeamFoundation.Framework.Common.ReadIdentityOptions]::TrueSid)
 $members = $ims.ReadIdentities($GroupIdentity.Members,[Microsoft.TeamFoundation.Framework.Common.MembershipQuery]::Direct,[Microsoft.TeamFoundation.Framework.Common.ReadIdentityOptions]::TrueSid)
 foreach($member in $members)
 {
  $row = $table.NewRow()

  #Enter data in the row
  $row.GroupName = $TFIdentity.DisplayName
  $row.DisplayName = $member.DisplayName
  $row.UniqueName = $member.UniqueName
  
  #Add the row to the table
  $table.Rows.Add($row)
 }
 
 return @(,$table )
}

function GetPermissions {
 [CmdletBinding()]
 [OutputType([System.Data.Datatable])]
 PARAM (
  [Parameter(Mandatory=$true, Position = 0)]
  $QueryName,
  [Parameter(Mandatory=$true, Position = 1)]
  [Microsoft.TeamFoundation.Framework.Client.AccessControlList]
  $acl,
  [Parameter(Mandatory=$true, Position = 2)]
  $namespace,
  [Parameter(Mandatory=$true, Position = 3)]
  $objectType,
  [Parameter(Mandatory=$true, Position = 4)]
  $objectPath
 )
 
 $PermissionstabName = $QueryName
 
 $table = New-Object system.Data.DataTable “$PermissionstabName”
 $col1 = New-Object system.Data.DataColumn ObjectPath,([string])
 $col2 = New-Object system.Data.DataColumn ObjectType,([string])
 $col3 = New-Object system.Data.DataColumn Name,([string])
 $col4 = New-Object system.Data.DataColumn Permission,([string])
 $col5 = New-Object system.Data.DataColumn Value,([string])

 #Add the Columns
 $table.columns.add($col1)
 $table.columns.add($col2)
 $table.columns.add($col3)
 $table.columns.add($col4)
 $table.columns.add($col5)

 foreach ($ace in $acl.AccessControlEntries)
 {
  if ($ace.IsEmpty)
  {
   continue
  }
  
  $haspermission = $false
  $DenyPermissions = @{}
  $CalculatedPermissions = @{}
  $AllowPermissions = @{}
  foreach ($action in $namespace.description.Actions)
  {
   
   $allowed = ($action.bit -band $ace.Allow) 
   $denied =  ($action.bit -band $ace.Deny)
   if (-not $allowed -and -not $denied)
   {
    continue
   }
   $haspermission  =$true
   if ($allowed)
   { 
    $CalculatedPermissions.Add($action.Name,"Allow")
    $AllowPermissions.Add($action.Name,$action.Name)
   }
   else
   {
    $CalculatedPermissions.Add($action.Name,"Deny")
    $DenyPermissions.Add($action.Name,$action.Name)
   }
   
  }
  
  if ($haspermission)
  {
   
   $identity = $ims.ReadIdentity($ace.Descriptor,[Microsoft.TeamFoundation.Framework.Common.MembershipQuery]::None,[Microsoft.TeamFoundation.Framework.Common.ReadIdentityOptions]::IncludeReadFromSource)
   
   $name = $identity.DisplayName
   
   Foreach($permission in $CalculatedPermissions.GetEnumerator())
   {
    $row = $table.NewRow()

    #Enter data in the row
    $row.ObjectPath = $objectPath 
    $row.ObjectType = $objectType
    $row.Name = $name
    $row.Permission = $permission.Name
    $row.Value = $permission.value

    #Add the row to the table
    $table.Rows.Add($row)
   }
  }
 }
 
 return @(,$table )
}

[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.Client')
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.VersionControl.Client')
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.WorkItemTracking.Client')
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.Build.Client')
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.Build.Common')
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation')

$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($serverName)

$css = $tfs.GetService([Microsoft.TeamFoundation.Server.ICommonStructureService])
$auth = $tfs.GetService([Microsoft.TeamFoundation.Server.IAuthorizationService])
$gss = $tfs.GetService([Microsoft.TeamFoundation.Server.IGroupSecurityService])
$ss = $tfs.GetService([Microsoft.TeamFoundation.Framework.Client.ISecurityService])
$vcs = $tfs.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer])
$ims = $tfs.GetService([Microsoft.TeamFoundation.Framework.Client.IIdentityManagementService])
$wis = $tfs.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]) 
$cssNamespace = $ss.GetSecurityNamespace([Microsoft.TeamFoundation.Server.AuthorizationSecurityConstants]::CommonStructureNodeSecurityGuid)

$membershipds = New-Object System.Data.DataSet
$permissiondt = New-Object System.Data.DataSet
$Objectpermissiondt = New-Object System.Data.DataSet

#get all TPC Groups
$tpcGroups = $IMS.ListApplicationGroups($null, [Microsoft.TeamFoundation.Framework.Common.ReadIdentityOptions]::None)

foreach ($tpcgroup in $tpcGroups)
{
 
 $members = GetGroupMembership $tpcgroup
 if ($members.rows.count -gt 0)
 {
  $membershipds.Tables.Add($members)
 }
}
$Namespaces = $ss.GetSecurityNamespaces()
#Get all the permissions for top level NameSpaces
foreach($namespace in $Namespaces)
{
 
 $NameSpaceToken = ""
 
    switch ($namespace.Description.DisplayName)
 {
  "Server" { $NameSpaceToken =  [Microsoft.TeamFoundation.Framework.Common.FrameworkSecurity]::FrameworkNamespaceToken}
  "Build" { $NameSpaceToken =  [Microsoft.TeamFoundation.Build.Common.BuildSecurity]::PrivilegesToken}
  "BuildAdministration" { $NameSpaceToken =  [Microsoft.TeamFoundation.Build.Common.BuildSecurity]::PrivilegesToken}
  "Workspaces" {  $NameSpaceToken =  [Microsoft.TeamFoundation.VersionControl.Common.SecurityConstants]::GlobalSecurityResource}
  "Collection" {}#Namespace:
  "WorkItemTrackingAdministration" {} #
  "CSS" {  }
  "Iteration" {}
  "VersionControlPrivileges" {$NameSpaceToken =  [Microsoft.TeamFoundation.VersionControl.Common.SecurityConstants]::GlobalSecurityResource} 
  "WorkItemQueryFolders" {}
  "Project" {  $NameSpaceToken = [Microsoft.TeamFoundation.PermissionNamespaces]::Project}
  default
  {
  continue
  }
 }
 try
 {
  $groupAcl = $namespace.QueryAccessControlList($NameSpaceToken,$null,$false)
  $table = GetPermissions $namespace.Discription.Name $groupAcl $namespace "Group" $namespace.Discription.Name
  $permissiondt.Tables.Add($table)
 }
 Catch
 {
 
 }
}

foreach ($project in $css.ListProjects())
{
 $projectGroups = $IMS.ListApplicationGroups($project.Uri,[Microsoft.TeamFoundation.Framework.Common.ReadIdentityOptions]::TrueSid)
  
 foreach($group in $projectGroups)
 {
 
  #only get the groups we care about
  if ($group.DisplayName -eq "[$($project.Name)]\Build Administrators" -or $group.DisplayName -eq "[$($project.Name)]\Project Administrators" -or $group.DisplayName -eq "[$($project.Name)]\Contributors" -or $group.DisplayName -eq "[$($project.Name)]\Readers")
  {
   
  }
  else
  {
   continue;
  }
  
  $members = GetGroupMembership $group
  if ($members.rows.count -gt 0)
  {
   $membershipds.Tables.Add($members)
  }
  
  $projectsecNameSpace = $ss.GetSecurityNamespace([Microsoft.TeamFoundation.Server.AuthorizationSecurityConstants]::ProjectSecurityGuid)
  
  #Get Project Permissions
  $ProjectSecurityToken = [Microsoft.TeamFoundation.Server.AuthorizationSecurityConstants]::ProjectSecurityPrefix + $project.Uri
  $groupacl = $projectsecNameSpace.QueryAccessControlList($ProjectSecurityToken,  [Microsoft.TeamFoundation.Framework.Client.IdentityDescriptor[]]@($group.descriptor), $false)
   
  $table =  GetPermissions $group.DisplayName  $groupAcl $projectsecNameSpace "Project" $project.Name
  $permissiondt.Tables.Add($table)
  
  #Get Top Level Build Permissions for the Project
  $BuildsecNameSpace = $ss.GetSecurityNamespace([Microsoft.TeamFoundation.Build.Common.BuildSecurity]::BuildNamespaceId)
  
  $teamProjectGuid = [Microsoft.TeamFoundation.LinkingUtilities]::DecodeUri($project.Uri).ToolSpecificId
  $groupacl = $BuildsecNameSpace.QueryAccessControlList($teamProjectGuid,  $null, $false)
  $tablename = $group.DisplayName + "Build Permissions"
  $table = GetPermissions $tablename $groupAcl $BuildsecNameSpace "Build" $project.Name
  
  $permissiondt.Tables.Add($table)
 }
 
 #Get Root Area Path Permissions
 $rootAreaNodeACL = $cssNamespace.QueryAccessControlList($wis.Projects[$project.Name].AreaRootNodeUri,$null,$false)
 $table = GetPermissions "$($project.Name) Root AreaPath Permissions" $rootAreaNodeACL  $cssNamespace "Area Path" $wis.Projects[$project.Name].Name
 $Objectpermissiondt.Tables.Add($table)

  
 #Get Child Area Path Permissions 
 foreach ($area in $wis.Projects[$project.Name].AreaRootNodes)
 {
  $areapath = $area.Path
  
  $areaseclist = $cssNamespace.QueryAccessControlList($area.uri, $null, $true)
  
  $table = GetPermissions "$areapath AreaPath Permissions" $areaseclist  $cssNamespace "Area Path" $areapath
  $Objectpermissiondt.Tables.Add($table)
 }
 
 try
 {
  $vcproject = $vcs.TryGetTeamProject($project.Name)
 }
 catch
 {
  continue
 }
 
 #Get Version Control permissions on all VC objects
 $vcAcls = $vcs.GetPermissions(@($vcproject.ServerItem), [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full)
  
 $table = New-Object system.Data.DataTable "$($project.Name)VCPermissions"
 $col1 = New-Object system.Data.DataColumn ObjectPath,([string])
 $col2 = New-Object system.Data.DataColumn ObjectType,([string])
 $col3 = New-Object system.Data.DataColumn Name,([string])
 $col4 = New-Object system.Data.DataColumn Permission,([string])
 $col5 = New-Object system.Data.DataColumn Value,([string])

 #Add the Columns
 $table.columns.add($col1)
 $table.columns.add($col2)
 $table.columns.add($col3)
 $table.columns.add($col4)
 $table.columns.add($col5)
 
 foreach($perm in $vcAcls)
 {
  foreach($entry in $perm.Entries)
  {
   foreach($allow in $entry.Allow)
   {
    
    $row = $table.NewRow()
    $row.ObjectPath = $perm.ServerItem
    $row.ObjectType = "VC"
    $row.Name = $entry.IdentityName
    $row.Permission = $allow
    $row.Value = "allow"
    
    #Add the row to the table
    $table.Rows.Add($row)
   }
   foreach($deny in $entry.Deny)
   {
    
    $row = $table.NewRow()
    $row.ObjectPath = $perm.ServerItem
    $row.ObjectType = "VC"
    $row.Name = $entry.IdentityName
    $row.Permission = $deny
    $row.Value = "deny"
    
    #Add the row to the table
    $table.Rows.Add($row)
   }
  }
 }
 
 $Objectpermissiondt.Tables.Add($table)
}

$membershipds.Tables | Format-Table -AutoSize 
$permissiondt.Tables | Format-Table -AutoSize 
$Objectpermissiondt.Tables | Format-Table -AutoSize

10 comments:

Unknown said...

Nice work on this I've adapted it a little bit:

$members = GetGroupMembership $group
if ($members.rows.count -gt 0)
{
$membershipds.Tables.Add($members)
}

to
$members = Get-GroupMemberShip $group
foreach ($member in $members)
{
if ($member.rows.count -gt 0)
{
if($membershipds.Tables.contains($member))
{Write-Verbose "$member already exists in the MembershipId's object"}
else {$membershipds.Tables.Add($member)}
}
}

In visual studio 2015 this class is no longer valid: Microsoft.TeamFoundation.Build.Common.BuildSecurity

Have you worked on this using the newer Visual Studio client?

Unknown said...

In addition I've posted some of this code in a GIST I call TFS-Functions here:

https://gist.github.com/crshnbrn66/73ea6364f6d4402d73a9

A lot of the code was based on this posting that you did. Nice work!

Unknown said...


I blog quite often and I genuinely thank you for your content. Your article has truly peaked my interest. I'm going to bookmark your website and keep checking for new information about once a week. I opted in for your Feed too. aol.com email sign in

Anonymous said...

Thanks for such a great article here. I was searching for something like this for quite a long time and at last I’ve found it on your blog. It was definitely interesting for me to read about their market situation nowadays pmp training in chennai | pmp training institute in chennai | pmp training centers in chennai| pmp training in velachery | pmp training near me | pmp training courses online |

Diya shree said...

Hi, Thanks a lot for your explanation which is really nice. I have read all your posts here. It is amazing!!!
Keeps the users interest in the website, and keep on sharing more, To know more about our service:
Please free to call us @ +91 9884412301 / 9600112302

Openstack course training in Chennai | best Openstack course in Chennai | best Openstack certification training in Chennai | Openstack certification course in Chennai | openstack training in chennai omr | openstack training in chennai velachery | openstack training in Chennai | openstack course fees in Chennai | openstack certification training in Chennai | best openstack training in Chennai | openstack certification in Chennai

Anonymous said...

Wow!! Really a nice Article. Thank you so much for your efforts. Definitely, it will be helpful for others. I would like to follow your blog. Share more like this. Thanks Again.
iot training in Chennai | Best iot Training Institute in Chennai

Ada Smith said...

wow cool site i bring out money here almost every week stunning free online slots Want also?Here is a link to luck

Diya shree said...

Good job and thanks for sharing such a good blog You’re doing a great job. Keep it up !!

PMP Training in Chennai | Best PMP Training in Chennai |
pmp certification cost in chennai | PMP Certification Training Institutes in Velachery |
pmp certification courses and books | PMP Certification requirements in Chennai | PMP Interview questions and answers

papa said...

GOOD ARTICLE

final year project
mini projects for cse
final year projects for cse
final year projects for cse students
final year projects for cse domains
final year projects for cse in data mining
final year projects for cse with source code
final year project for ece
final year project in mechanical engineering
final year project for eee

invincible01 said...

Amazing Article,Really useful information to all So, I hope you will share more information to be check and share here.


inplant training for biotechnology in chennai
inplant training for ece students
inplant training mechanical engineering students
inplant training certificate format for civil engineering
inplant training report ppt
inplant training report samples
inplant training letter format
inplant training report for civil engineering pdf
inplant training report for electrical engineering