To perform certain actions on Azure DevOps including, process template changes, extension import, build modification etc. requires user to have privilege's scopes at either organization or project level. In large enterprises where Azure DevOps organizations are centrally managed or managed via service accounts, enabling permanent privilege access for group of members posses lot of security risk. In scenario's where organizations are created using service account, require user to login via same to perform privilege actions. This posses a security risk of sharing credentials with multiple users and traceability of action performed.
Since there is no out of box solution available to enable just in time access with privilege role alike "Azure PIM", there is a custom approach can be used to enable just in time experience and control.
Technology involved:
Approach:
Steps:
1. Login to Azure DevOps using service account having owner access across Azure DevOps organization managed by team.
2. Create a personal access token with below scopes across "All accessible organization"
permissions: vso.graph_manage vso.project.
3. Create addUser.ps1 as below:
This script will add user to respective organization/project with required privilege role and enter the data within Azure Sql server.
Param
(
[Parameter(Mandatory=$true,HelpMessage = "Org name for which access is requeseted")]
[string][ValidateNotNullOrEmpty()] $OrgName,
[Parameter(Mandatory=$true,HelpMessage = "Operation for which request is raised")]
[string][ValidateNotNullOrEmpty()] $RequestType,
[Parameter(Mandatory=$true,HelpMessage = "email address for which access should be provded")]
$UserEmailAddress,
[Parameter(Mandatory=$true,HelpMessage = "Application Insights details")]
[string][ValidateNotNullOrEmpty()] $LoggingTelemetryID,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $PAT,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLPassword,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLUserID,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLDataBase,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLServer,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $UserDescriptorOrganization,
[Parameter(Mandatory=$false,HelpMessage = "Project name for which access is requeseted")]
[string] $ProjectName
)
#####Creating session id####
try
{
$sessionId = [guid]::NewGuid().GUID
}
catch
{
Write-Error "Failed to create session id"
Write-Output $_.Exception.Message
}
###Initializing telemetry client###
try
{
$AppInsightsdllPath = "$PSScriptRoot\Microsoft.ApplicationInsights.dll"
[Reflection.Assembly]::LoadFile($AppInsightsdllPath)
$TelClient = New-Object "Microsoft.ApplicationInsights.TelemetryClient"
$TelClient.InstrumentationKey = $LoggingTelemetryID
$credentials = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$PAT"))
$SQLRetryCount = 0
}
catch
{
Write-Error "Failed to initialize ApplicationInsights"
Write-Output $_.Exception.Message
}
####Function to get user descriptor which will be used in adding function####
function Get_User_Descriptor
{
try
{
$ContinuationToken=$null
do
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$usersList= Invoke-webrequest -Method Get -Uri "https://vssps.dev.azure.com/$($UserDescriptorOrganization)/_apis/graph/users?api-version=6.0-preview.1&subjectTypes=aad&continuationToken=$($ContinuationToken)" -Headers @{Authorization = $credentials } -ContentType 'application/json' -UseBasicParsing
$APIContents = $usersList.Content | ConvertFrom-Json
foreach($user in $APIContents.value)
{
if($user.principalName -eq $UserEmailAddress)
{
return $user.descriptor
}
}
$ContinuationToken = $usersList.Headers.'x-ms-continuationtoken'
} while ($ContinuationToken -ne $null)
}
catch
{
Write-Error "Failed to get user descriptor"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
#### Function to add user to Org####
function Add_Users
{
param (
[string]$UserDescriptor
)
try
{
$checkUser= Invoke-webrequest -Method Get -Uri "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/users/$($UserDescriptor)?api-version=6.0-preview.1" -Headers @{Authorization = $credentials } -ContentType 'application/json' -UseBasicParsing
}
catch
{
if($_.Exception.Message -match "(401)")
{
Write-Error "gdbldsvc don't have access to ADO $($OrgName)"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
elseif($_.Exception.Message -match "(404)")
{
try
{
$UserBody = '{"principalName":'+'"'+$UserEmailAddress+'"}'
$addUser= Invoke-webrequest -Method POST -Body $UserBody -Uri "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/users/$($UserDescriptor)?api-version=6.0-preview.1" -Headers @{Authorization = $credentials } -ContentType 'application/json' -UseBasicParsing
return $UserDescriptor
}
catch
{
Write-Error "Failed to add user to ADO"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
else
{
Write-Error "Failed to add users to ADO $($OrgName)"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
}
#### Function to add user to organization####
function Add_UsersTo_Org_PCA
{
param (
[string]$OrganizationName
)
try
{
$userDescriptor = Get_User_Descriptor
Add_Users -UserDescriptor $userDescriptor
do
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$grouplist= Invoke-webrequest -Method Get -Uri "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/groups?api-version=6.0-preview.1&continuationToken=$($ContinuationToken)" -Headers @{Authorization = $credentials } -ContentType 'application/json' -UseBasicParsing
$ContinuationToken = $grouplist.Headers.'x-ms-continuationtoken'
$APIContents = $grouplist.Content | ConvertFrom-Json
foreach($group in $APIContents.value)
{
try
{
if("[$OrgName]\Project Collection Administrators" -eq $group.principalName)
{
$groupdescriptor = $group.descriptor
$url = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/memberships/$($userDescriptor)/$($groupdescriptor)?api-version=6.0-preview.1"
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$result= Invoke-RestMethod -Method PUT -Uri $url -ContentType 'application/json' -Headers @{Authorization = $credentials } -ErrorAction Continue -UseBasicParsing
Add_Roleassignment_database -Roleassignment $group.principalName
$TelClient.TrackEvent("Added $($UserEmailAddress) to $($group.principalName)")
$TelClient.Flush()
$ContinuationToken=$null
break
}
}
catch
{
Write-Error "Failed to users as Project Collection Administrators"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
} while ($null -ne $ContinuationToken)
}
catch
{
Write-Error "Failed to get group details for the organization"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
####Function to add user to project####
function Add_UsersTo_Project
{
param (
[string]$OrganizationName,
[string]$ProjectName
)
$userDescriptor = Get_User_Descriptor
Add_Users -UserDescriptor $userDescriptor
try
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$projectList = Invoke-RestMethod -Uri "https://dev.azure.com/$($OrganizationName)/_apis/projects?`$top=999&api-version=6.0" -Headers @{Authorization = $credentials } -Method Get -ContentType 'application/json'
foreach($project in $projectList.value)
{
if($ProjectName -eq $project.name)
{
try
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$projectDetails = Invoke-RestMethod -Uri "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/descriptors/$($project.id)?api-version=5.0-preview.1" -Headers @{Authorization = $credentials } -Method Get -ContentType 'application/json'
$GroupsUrl = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/groups?scopeDescriptor=$($projectDetails.value)&api-version=6.0-preview.1"
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$grouplist= Invoke-RestMethod -Uri $GroupsUrl -Headers @{Authorization = $credentials } -Method Get -ContentType 'application/json'
}
catch
{
Write-Error "Failed to get group details for project"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
try
{
foreach($group in $grouplist.value)
{
if(("[$($project.name)]\Build Administrators" -eq $group.principalName) -or ("[$($project.name)]\Endpoint Administrators" -eq $group.principalName))
{
$groupdescriptor = $group.descriptor
$url = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/memberships/$($userDescriptor)/$($groupdescriptor)?api-version=6.0-preview.1"
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$result= Invoke-RestMethod -Method PUT -Uri $url -ContentType 'application/json' -Headers @{Authorization = $credentials } -ErrorAction Continue
Add_Roleassignment_database -Roleassignment $group.principalName
$TelClient.TrackEvent("Added $($UserEmailAddress) to $($group.principalName)")
$TelClient.Flush()
}
}
break
}
catch
{
Write-Error "Failed to add user to the project"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
}
}
catch
{
Write-Error "Failed to get project details for organization"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
####Function to log activity to sql database####
function Add_Roleassignment_database
{
param(
[string] $Roleassignment
)
try
{
$SQLConnectionString = "Server=tcp:$($SQLServer),1433;Initial Catalog=$($SQLDataBase);Persist Security Info=False;User ID=$($SQLUserID);Password=$($SQLPassword);MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
$sqlCommand = New-Object System.Data.SqlClient.SqlCommand
$sqlCommand.Connection = $SQLConnectionString
[DateTime] $date= Get-Date -Format "yyyy-MM-dd HH:mm:ss"
## SQL Query
$sqlCommand.CommandText = "SET NOCOUNT ON; " +
"INSERT INTO dbo.ADOAccessLogs (SessionID,RequesterEmailAddress,OrganizationName,ProjectName,RequestType,AccessInitiationTime,IsActive, RoleType) " +
"VALUES ('$sessionId','$UserEmailAddress','$OrgName','$ProjectName','$RequestType','$($date.ToUniversalTime())',1,'$Roleassignment')";
# Run the query and get the scope ID back into $InsertedID
$sqlCommand.Connection.Open()
$sqlCommand.ExecuteScalar()
$sqlCommand.Connection.Close()
}
catch
{
Write-Error "Failed to insert access logs to Database"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
$SQLRetryCount++
if($SQLRetryCount -lt 6)
{
Add_Roleassignment_database -Roleassignment $Roleassignment
}
}
}
####Request type validation####
if( ($RequestType -eq "Process_Template_Change") -or ($RequestType -eq "Project_Creation"))
{
try
{
###Adding user to organization###
Add_UsersTo_Org_PCA -OrganizationName $OrgName
$TelClient.TrackEvent("Added $($UserEmailAddress) to $($OrgName) as Project Collection Administrators")
$TelClient.Flush()
}
catch
{
Write-Error "Failed to users as Project Collection Administrators"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
elseif( ($RequestType -ieq "Build_troubleshooting") -or ($RequestType -eq "Build_creation" ))
{
if($ProjectName -eq $null)
{
Write-Error "Please provide Project details"
$TelClient.TrackException("Project details is missing")
$TelClient.Flush()
break
}
try
{
###Adding user to project###
Add_UsersTo_Project -OrganizationName $OrgName -ProjectName $ProjectName
$TelClient.TrackEvent("Added $($UserEmailAddress) to $($ProjectName)")
$TelClient.Flush()
}
catch
{
Write-Error "Failed while provding Build Administrator Permission"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
else
{
Write-Output "Please provide valid justification for ADO access"
$TelClient.TrackException("Invalid justification provided for ADO access, email address : $($UserEmailAddress)" )
$TelClient.Flush()
}
4. Create a Azure Sql server and database. Reference: https://docs.microsoft.com/en-us/azure/azure-sql/database/single-database-create-quickstart?tabs=azu...
5. Create a application insight resource. Reference: https://docs.microsoft.com/en-us/azure/azure-monitor/app/create-new-resource
6. Store below credentials in Azure key vault. Reference: https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-portal
Secret: serviceAccountPAT, Value: Personal access token created at step 1
Secret: applicationInsightKey, Value: Instrumentation key created at step 5
Secret: SqlDatabaseName, Value: Sql details as created at step 4
Secret: SqlServerName, Value: Sql details as created at step 4
Secret: SqlAdminUserName, Value: Sql details as created at step 4
Secret: SqlAdminPassword, , Value: Sql details as created at step 4
7. Create a variable group in Azure DevOps and link all the secrets created at step 6 as variables. Reference: https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&ta...
8. Create a release pipeline on Azure DevOps with pre-deployment enabled. This pre-deployment will be used to approve or deny request by administrator. Reference: https://docs.microsoft.com/en-us/learn/modules/create-release-pipeline/
9. Use below task in release pipeline to execute addUser.ps1 script
variables:
UserDescriptorOrganization: '$(OrganizationName)'
steps:
- task: PowerShell@2
displayName: 'PowerShell Script'
inputs:
targetType: filePath
filePath: './$(System.DefaultWorkingDirectory)/_azureDevOpsJitAccess/addUser.ps1'
arguments: '-OrgName $(OrganizationName) -RequestType $(RequestType) -UserEmailAddress $(EmailAddress) -LoggingTelemetryID $(applicationInsightKey) -PAT $(serviceAccountPAT) -ProjectName $(Project) -SQLPassword $(SqlAdminPassword) -SQLUserID $(SqlAdminUserName) -SQLDataBase $(SqlDatabaseName) -SQLServer $(SqlServerName) -UserDescriptorOrganization $(UserDescriptorOrganization)'
8. Add below variables as pipeline variable which will be used by user or executor to pass runtime variable.
EmailAddress - This contain email id of user whom just in time privilege access need to be enabled.
OrganizationName - Organization name where user is seeking privilege role.
Project - Project name where user is seeking access.
RequestType - This contain activity type which user want to perform and for which access is requested. Script as part of step 3, will check request type and based on that provide user with required permission.
UserDescriptorOrganization - This contain organization name where user is currently part of to extract user descriptor and accordingly add in destination/requested organization.
9. Create a script removeUser.ps1 to remove user after certain time. Below script will remove all users having permission more than 30min.
Note: Below script will read the database and fetch all users having access longer than 30 min and remove the same. To validate if user is removed, it will update IsActive column as 0 from 1.
Param
(
[Parameter(Mandatory=$true,HelpMessage = "Application Insights details")]
[string][ValidateNotNullOrEmpty()] $LoggingTelemetryID,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $PAT,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLPassword,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLUserID,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLDataBase,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $SQLServer,
[Parameter(Mandatory=$true)]
[string][ValidateNotNullOrEmpty()] $UserDescriptorOrganization
)
###Initializing telemetry client and Varibales###
try
{
$AppInsightsdllPath = "$PSScriptRoot\Microsoft.ApplicationInsights.dll"
[Reflection.Assembly]::LoadFile($AppInsightsdllPath)
$TelClient = New-Object "Microsoft.ApplicationInsights.TelemetryClient"
$TelClient.InstrumentationKey = $LoggingTelemetryID
$credentials = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$PAT"))
$SQLConnectionString = "Server=tcp:$($SQLServer),1433;Initial Catalog=$($SQLDataBase);Persist Security Info=False;User ID=$($SQLUserID);Password=$($SQLPassword);MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
}
catch
{
Write-Error "Failed to initialize ApplicationInsights"
Write-Output $_.Exception.Message
}
####Get past 30 min active role data####
function Get-ActiveAssignment-Database
{
try
{
[DateTime] $date= Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$query = "SELECT * FROM ADOAccessLogs where IsActive = 1 and AccessInitiationTime < '$($date.AddMinutes(-30).ToUniversalTime())'"
$Datatable = New-Object System.Data.DataTable
$command = New-Object System.Data.SqlClient.SqlCommand
$command.Connection = $SQLConnectionString
$command.CommandText = $query
$command.Connection.Open()
$results = $command.ExecuteReader()
$Datatable.Load($results)
$command.Connection.Close()
return $Datatable;
}
####Catch SQL Exception####
catch
{
Write-Error "Failed to get access logs from Database"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
####Function to get user descriptor which will be removed####
function Get-User-Descriptor
{
param(
[string] $UserEmailAddress
)
try
{
$ContinuationToken=$null
do{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$usersList= Invoke-webrequest -Method Get -Uri "https://vssps.dev.azure.com/$($UserDescriptorOrganization)/_apis/graph/users?api-version=6.0-preview.1&subjectTypes=aad&continuationToken=$($ContinuationToken)" -Headers @{Authorization = $credentials } -ContentType 'application/json' -UseBasicParsing
$APIContents = $usersList.Content | ConvertFrom-Json
foreach($user in $APIContents.value)
{
if($user.principalName -eq $UserEmailAddress)
{
return $user.descriptor
}
}
$ContinuationToken = $usersList.Headers.'x-ms-continuationtoken'
} while ($null -ne $ContinuationToken)
}
catch
{
Write-Error "Failed to get group details for the organization"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
function Remove-AccessFor-Users
{
param (
[object]$ActiveAssignments
)
try
{
if($null -eq $ActiveAssignments)
{
Write-Output "No Active Role Assignments Found"
$TelClient.TrackEvent("No Active Role Assignments Found")
$TelClient.Flush()
break
}
foreach($ActiveAssignment in $ActiveAssignments)
{
if($ActiveAssignment.RoleType -match "Project Collection Administrators")
{
Remove-Access-From-Org -EmailAddress $ActiveAssignment.RequesterEmailAddress -OrgName $ActiveAssignment.OrganizationName -SessionID $ActiveAssignment.SessionID -RoleType $ActiveAssignment.RoleType
}
elseif(($ActiveAssignment.RoleType -match "Build Administrators") -or ($ActiveAssignment.RoleType -match "Endpoint Administrators") )
{
Remove-Access-From-Project -EmailAddress $ActiveAssignment.RequesterEmailAddress -OrgName $ActiveAssignment.OrganizationName -SessionID $ActiveAssignment.SessionID -RoleType $ActiveAssignment.RoleType -ProjectName $ActiveAssignment.ProjectName
}
else
{
Write-Error "Invaid RoleType : $($ActiveAssignment.RoleType)"
$TelClient.TrackException("Invaid RoleType : $($ActiveAssignment.RoleType)")
$TelClient.Flush()
}
}
}
catch
{
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
function Remove-Access-From-Org
{
param (
[string]$EmailAddress,
[String]$OrgName,
[String]$SessionID,
[String]$RoleType
)
try
{
$userDescriptor= Get-User-Descriptor -UserEmailAddress $EmailAddress
do
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$grouplist= Invoke-webrequest -Method Get -Uri "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/groups?api-version=6.0-preview.1&continuationToken=$($ContinuationToken)" -Headers @{Authorization = $credentials } -ContentType 'application/json' -UseBasicParsing
$ContinuationToken = $grouplist.Headers.'x-ms-continuationtoken'
$APIContents = $grouplist.Content | ConvertFrom-Json
foreach($group in $APIContents.value)
{
try
{
if("[$OrgName]\Project Collection Administrators" -eq $group.principalName)
{
$groupdescriptor = $group.descriptor
$url = "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/memberships/$($userDescriptor)/$($groupdescriptor)?api-version=6.0-preview.1"
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-RestMethod -Method DELETE -Uri $url -ContentType 'application/json' -Headers @{Authorization = $credentials } -ErrorAction Continue -UseBasicParsing
Update_Roleassignment_database -Email $EmailAddress -SessionID $SessionID -RoleType $RoleType
$TelClient.TrackEvent("Added $($UserEmailAddress) to $($group.principalName)")
$TelClient.Flush()
$ContinuationToken=$null
break
}
}
catch
{
Write-Error "Failed to remove user $($EmailAddress) from Project Collection Administrators"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
} while ($null -ne $ContinuationToken)
}
catch
{
Write-Error "Failed to get group details from Org : $(OrgName)"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
function Remove-Access-From-Project
{
param (
[string]$EmailAddress,
[String]$OrgName,
[String]$ProjectName,
[String]$SessionID,
[String]$RoleType
)
$userDescriptor = $userDescriptor= Get-User-Descriptor -UserEmailAddress $EmailAddress
try
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$projectList = Invoke-RestMethod -Uri "https://dev.azure.com/$($OrgName)/_apis/projects?`$top=999&api-version=6.0" -Headers @{Authorization = $credentials } -Method Get -ContentType 'application/json'
foreach($project in $projectList.value)
{
if($ProjectName -eq $project.name)
{
try
{
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$projectDetails = Invoke-RestMethod -Uri "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/descriptors/$($project.id)?api-version=5.0-preview.1" -Headers @{Authorization = $credentials } -Method Get -ContentType 'application/json'
$GroupsUrl = "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/groups?scopeDescriptor=$($projectDetails.value)&api-version=6.0-preview.1"
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$grouplist= Invoke-RestMethod -Uri $GroupsUrl -Headers @{Authorization = $credentials } -Method Get -ContentType 'application/json'
}
catch
{
Write-Error "Failed to get group details for project"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
try
{
foreach($group in $grouplist.value)
{
if(($RoleType -eq $group.principalName) -or ($RoleType -eq $group.principalName))
{
$groupdescriptor = $group.descriptor
$url = "https://vssps.dev.azure.com/$($OrgName)/_apis/graph/memberships/$($userDescriptor)/$($groupdescriptor)?api-version=6.0-preview.1"
## Set communication protol to TLS1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-RestMethod -Method DELETE -Uri $url -ContentType 'application/json' -Headers @{Authorization = $credentials } -ErrorAction Continue
$TelClient.TrackEvent("Removed $($UserEmailAddress) from $($group.principalName)")
$TelClient.Flush()
## Updated status in DataBase
Update_Roleassignment_database -Email $EmailAddress -SessionID $SessionID -RoleType $RoleType
}
}
break
}
catch
{
Write-Error "Failed to Delete user $($EmailAddress) from the project : $($ProjectName) Group : $($RoleType)"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
}
}
catch
{
Write-Error "Failed to get project details for organization"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
function Update_Roleassignment_database
{
param (
[string]$Email,
[String]$SessionID,
[String]$RoleType
)
try
{
$query = "UPDATE ADOAccessLogs SET IsActive = 0 where SessionID='$($SessionID)' AND RoleType='$($RoleType)'"
$command = New-Object System.Data.SqlClient.SqlCommand
$command.Connection = $SQLConnectionString
$command.CommandText = $query
$command.Connection.Open()
$command.ExecuteReader()
$command.Connection.Close()
}
catch
{
Write-Error "Failed to update active status for User : $($Email) SessionID : $($SessionID)"
Write-Output $_.Exception.Message
$TelClient.TrackException($_.Exception)
$TelClient.Flush()
}
}
$ActiveUsers = $null
$ActiveUsers = Get-ActiveAssignment-Database | Sort-Object ID -Unique
$ActiveUsers
Remove-AccessFor-Users -ActiveAssignments $ActiveUsers
10. Create a build pipeline using below YAML. This will require variable group to be linked, created as part of step 7
schedules:
- cron: "*/10 * * * *"
displayName: Daily build for every 30 min
branches:
include:
- master
always: true
pool:
Image: 'windows-2019'
steps:
- task: PowerShell@2
displayName: 'PowerShell Script'
inputs:
filePath: './removeUser.ps1'
arguments: '-LoggingTelemetryID $(applicationInsightKey) -PAT $(serviceAccountPAT) -SQLPassword $(SqlAdminPassword) -SQLUserID $(SqlAdminUserName) -SQLDataBase $(SqlDatabaseName) -SQLServer $(SqlServerName) -UserDescriptorOrganization $(UserDescriptorOrganization)'
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.