Framework/Core/SVT/SVTResourceResolver.ps1

Set-StrictMode -Version Latest

class SVTResourceResolver: AzSKRoot {
    [string[]] $ResourceNames = @();
    [string] $ResourceType = "";
    [ResourceTypeName] $ResourceTypeName = [ResourceTypeName]::All;
    [Hashtable] $Tag = $null;
    [string] $TagName = "";
    [string[]] $TagValue = "";
    hidden [string[]] $ResourceGroups = @();
    [ResourceTypeName] $ExcludeResourceTypeName = [ResourceTypeName]::All;
    [string[]] $ExcludeResourceNames = @();
    [SVTResource[]] $ExcludedResources = @();
    [int] $MaxObjectsToScan;
    [SVTResource[]] $SVTResources = @();
    [int] $SVTResourcesFoundCount = 0;

    [bool] $IsAIEnabled = $false;
    [bool] $IsAutomatedFixUndoCmd = $false;

    [string] $ResourcePath;
    [string] $BuildsFolderPath;
    [string] $ReleasesFolderPath;
    [bool] $BatchScan;
    [string] $organizationName
    hidden [string[]] $ProjectNames = @();
    hidden [string[]] $BuildNames = @();
    hidden [string[]] $ReleaseNames = @();
    hidden [string[]] $AgentPools = @();
    hidden [string[]] $ServiceConnections = @();
    hidden [string[]] $VariableGroups = @();
    hidden [PSObject] $ControlSettings;
    #Local variable for longrunningscan for command parameter
    [bool] $allowLongRunningScan = $false
    #Local variables for longrunningscan for controlsettings variables
    [bool] $isAllowLongRunningScanInPolicy = $true
    [int] $longRunningScanCheckPoint = 1000;

    hidden [string[]] $serviceIds = @();

    [bool] $includeAdminControls = $false;
    [bool] $isUserPCA = $false;
    [bool] $skipOrgUserControls = $false

    [bool] $UseIncrementalScan = $false
    [DateTime] $IncrementalDate = 0

    [bool] $UsePartialCommits=$false;
    [bool] $DoNotRefetchResources=$false;
    [bool] $isPartialScanActive=$false;
    [PSObject] $nonScannedResources;

    hidden [string[]] $BuildIds = @();
    hidden [string[]] $ReleaseIds = @();
    hidden [string[]] $AgentPoolIds = @();
    hidden [string[]] $ServiceConnectionIds = @();
    hidden [string[]] $VariableGroupIds = @();
    hidden [bool] $isServiceIdBasedScan = $false;

    #Common svt resources
    hidden [string[]] $RepoNames = @();
    hidden [string[]] $SecureFileNames = @();
    hidden [string[]] $FeedNames = @();
    hidden [string[]] $EnvironmentNames = @();



    SVTResourceResolver([string]$organizationName, $ProjectNames, $BuildNames, $ReleaseNames, $AgentPools, $ServiceConnectionNames, $VariableGroupNames, $MaxObj, $ScanAllResources, $PATToken, $ResourceTypeName, $AllowLongRunningScan, $ServiceIds, $IncludeAdminControls, $skipOrgUserControls, $RepoNames, $SecureFileNames, $FeedNames, $EnvironmentNames,$BuildsFolderPath,$ReleasesFolderPath,$UsePartialCommits,$DoNotRefetchResources,$BatchScan, $UseIncrementalScan, $IncrementalDate): Base($organizationName, $PATToken) {


        $this.MaxObjectsToScan = $MaxObj #default = 0 => scan all if "*" specified...
        $this.SetallTheParamValues($organizationName, $ProjectNames, $BuildNames, $ReleaseNames, $AgentPools, $ServiceConnectionNames, $VariableGroupNames, $ScanAllResources, $PATToken, $ResourceTypeName, $AllowLongRunningScan, $ServiceIds, $IncludeAdminControls, $BuildsFolderPath,$ReleasesFolderPath,$UsePartialCommits,$DoNotRefetchResources,$BatchScan, $UseIncrementalScan, $IncrementalDate);
        $this.skipOrgUserControls = $skipOrgUserControls

        $this.RepoNames += $this.ConvertToStringArray($RepoNames);
        $this.SecureFileNames += $this.ConvertToStringArray($SecureFileNames);
        $this.FeedNames += $this.ConvertToStringArray($FeedNames);

        [PartialScanManager]::ClearInstance();
    

        $this.EnvironmentNames += $this.ConvertToStringArray($EnvironmentNames);
    }

    #Constructor for Set-AzSKADOSecurityStatus
    SVTResourceResolver([string]$organizationName, $ProjectNames, $ResourceNames, $ExcludeResourceNames, $PATToken, $ResourceTypeName, $UndoFix, $ControlId): Base($organizationName, $PATToken) {
        if($UndoFix) {
            if (!$this.ControlSettings) {
                $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json");
            }
            if($this.ControlSettings.AutomatedFix.RevertDeletedResourcesControlList -contains $ControlId)
            {
                #When controls undo fix is called, resources need to be fetched from deleted list (only for controls ids in RevertDeletedResourcesControlList)
                $this.IsAutomatedFixUndoCmd = $true
            }
        }
        $this.organizationName = $organizationName
        $this.ProjectNames = $ProjectNames
        $this.ResourceTypeName = $ResourceTypeName

        if (-not [string]::IsNullOrEmpty($ResourceNames)) {
            $this.ResourceNames += $this.ConvertToStringArray($ResourceNames);
        }
        if (-not [string]::IsNullOrEmpty($ExcludeResourceNames)) {
                $this.ExcludeResourceNames += $this.ConvertToStringArray($ExcludeResourceNames);
        }
        $this.SetallTheParamValues($ResourceTypeName)
    }


    [void] SetallTheParamValues([string]$organizationName, $ProjectNames, $BuildNames, $ReleaseNames, $AgentPools, $ServiceConnectionNames, $VariableGroupNames, $ScanAllResources, $PATToken, $ResourceTypeName, $AllowLongRunningScan, $ServiceIds, $IncludeAdminControls,$BuildsFolderPath,$ReleasesFolderPath,$UsePartialCommits,$DoNotRefetchResources,$BatchScan, $UseIncrementalScan, $IncrementalDate) {

        $this.organizationName = $organizationName
        $this.ResourceTypeName = $ResourceTypeName
        $this.allowLongRunningScan = $AllowLongRunningScan
        $this.includeAdminControls = $IncludeAdminControls
        $this.BuildsFolderPath = $BuildsFolderPath.Trim()
        $this.UsePartialCommits=$UsePartialCommits
        $this.DoNotRefetchResources=$DoNotRefetchResources
        $this.ReleasesFolderPath = $ReleasesFolderPath.Trim()
        $this.UseIncrementalScan = $UseIncrementalScan
        
        if (-not [string]::IsNullOrWhiteSpace($IncrementalDate)) 
        {
            $this.IncrementalDate = $IncrementalDate    
        }
        else 
        {
            $this.IncrementalDate = [datetime] 0    
        }
        $this.BatchScan = $BatchScan

        if (-not [string]::IsNullOrEmpty($ProjectNames)) {
            $this.ProjectNames += $this.ConvertToStringArray($ProjectNames);

            if ($this.ProjectNames.Count -eq 0) {
                throw [SuppressedException] "The parameter 'ProjectNames' does not contain any string."
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Project -or $ResourceTypeName -eq [ResourceTypeName]::Org_Project_User) {
            $this.ProjectNames = "*"
        }

        if (-not [string]::IsNullOrEmpty($BuildNames)) {
            $this.BuildNames += $this.ConvertToStringArray($BuildNames);
            if ($this.BuildNames.Count -eq 0) {
                throw [SuppressedException] "The parameter 'BuildNames' does not contain any string."
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Build -or $ResourceTypeName -eq [ResourceTypeName]::Build_Release) {
            $this.BuildNames = "*"
        }

        if (-not [string]::IsNullOrEmpty($ReleaseNames)) {
            $this.ReleaseNames += $this.ConvertToStringArray($ReleaseNames);
            if ($this.ReleaseNames.Count -eq 0) {
                throw [SuppressedException] "The parameter 'ReleaseNames' does not contain any string."
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Release -or $ResourceTypeName -eq [ResourceTypeName]::Build_Release) {
            $this.ReleaseNames = "*"
        }

        if (-not [string]::IsNullOrEmpty($ServiceConnectionNames)) {
            $this.ServiceConnections += $this.ConvertToStringArray($ServiceConnectionNames);

            if ($this.ServiceConnections.Count -eq 0) {
                throw [SuppressedException] "The parameter 'ServiceConnectionNames' does not contain any string."
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::ServiceConnection) {
            $this.ServiceConnections = "*"
        }

        if (-not [string]::IsNullOrEmpty($AgentPools)) {
            $this.AgentPools += $this.ConvertToStringArray($AgentPools);
            if ($this.AgentPools.Count -eq 0) {
                throw [SuppressedException] "The parameter 'AgentPools' does not contain any string."
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::AgentPool) {
            $this.AgentPools = "*"
        }

        if (-not [string]::IsNullOrEmpty($VariableGroupNames)) {
            $this.VariableGroups += $this.ConvertToStringArray($VariableGroupNames);

            if ($this.VariableGroups.Count -eq 0) {
                throw [SuppressedException] "The parameter 'VariableGroupNames' does not contain any string."
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::VariableGroup) {
            $this.VariableGroups = "*"
        }

        if (-not [string]::IsNullOrEmpty($ServiceIds)) {
            $this.serviceIds += $this.ConvertToStringArray($ServiceIds);
            if ($this.serviceIds.Count -eq 0) {
                throw [SuppressedException] "The parameter 'ServiceId' does not contain any string."
            }
        }

        #User should always provide project name (comma separated list or '*') to scan builds in an org. Else no controls will be scanned if -rtn is 'Build'
        #if (-not [string]::IsNullOrEmpty($ResourceTypeName) -and $ResourceTypeName -ne "All" -and ([string]::IsNullOrEmpty($ProjectNames))) {
        # $this.ProjectNames = "*"
        #}

        if ($ScanAllResources -and [string]::IsNullOrEmpty($ServiceIds)) {
            #ScanAllResources should scan all artifacts within the targeted projects (if provided explicitly)
            if ([string]::IsNullOrEmpty($ProjectNames)) {
                $this.ProjectNames = "*"
            }
            $this.BuildNames = "*"
            $this.ReleaseNames = "*"
            $this.AgentPools = "*"
            $this.ServiceConnections = "*"
            $this.VariableGroups = "*"
            $this.RepoNames = "*"
            $this.SecureFileNames = "*"
            $this.FeedNames = "*"
            $this.EnvironmentNames = "*"
        }

        if (( $this.MaxObjectsToScan -eq 0 -or $this.MaxObjectsToScan -gt $this.longRunningScanCheckPoint) -and ($this.ProjectNames -eq "*" -or $this.BuildNames -eq "*" -or $this.ReleaseNames -eq "*" -or $this.ServiceConnections -eq "*" -or $this.AgentPools -eq "*" -or $this.VariableGroups -eq "*")) {
            $this.PublishCustomMessage("Using '*' can take a long time for the scan to complete in larger projects. `nYou may want to provide a comma-separated list of projects, builds, releases, service connections, agent pools and variable groups. `n ", [MessageType]::Warning);
            <# BUGBUG: [Aug-2020] Removing this until we can determine the right approach to init org-policy-url for ADO.
            if (!$this.ControlSettings) {
                $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json");
            }
            #fetch control settings to check whether large scans are allowed in the org
            $this.isAllowLongRunningScanInPolicy = $this.ControlSettings.IsAllowLongRunningScan;
            $this.longRunningScanCheckPoint = $this.ControlSettings.LongRunningScanCheckPoint;
            #>


        }
        if($this.UseIncrementalScan -eq $true)
        {
            $this.PublishCustomMessage("Incremental Scan is currently supported only for Builds and Releases. `n ", [MessageType]::Warning);
        
            if($this.UsePartialCommits -ne $true)
            {
                $this.PublishCustomMessage("Using Incremental Scan without Partial Scan. In case of incomplete scan, the latest updated timestamp will be used for consequent incremental scans. `n ", [MessageType]::Warning);
            }
        }
    }


    # Method called for Set-AzSKADOSecurityStatus, invoked from constructor

    [void] SetallTheParamValues($ResourceTypeName) {
    
        if ($ResourceTypeName -eq [ResourceTypeName]::Build ) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.BuildNames = "*"
            }
            else{
                $this.BuildNames = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Release) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.ReleaseNames = "*"
            }
            else{
                $this.ReleaseNames = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::ServiceConnection) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.ServiceConnections = "*"
            }
            else{
                $this.ServiceConnections = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::AgentPool) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.AgentPools = "*"
            }
            else{
                $this.AgentPools = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::VariableGroup) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.VariableGroups = "*"
            }
            else{
                $this.VariableGroups = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Repository) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.RepoNames = "*"
            }
            else{
                $this.RepoNames = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::SecureFile) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.SecureFileNames = "*"
            }
            else{
                $this.SecureFileNames = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Feed) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.FeedNames = "*"
            }
            else{
                $this.FeedNames = $this.ResourceNames
            }
        }
        elseif ($ResourceTypeName -eq [ResourceTypeName]::Environment) {
            if([string]::IsNullOrWhitespace($this.ResourceNames))
            {
                $this.EnvironmentNames = "*"
            }
            else{
                $this.EnvironmentNames = $this.ResourceNames
            }
        }
        
    }

    [void] LoadResourcesForScan() {
        #Call APIS for Organization,User/Builds/Releases/ServiceConnections
        $organizationId = "";

        if ([RemoteReportHelper]::IsAIOrgTelemetryEnabled()) {
            $this.IsAIEnabled = $true;
        }

        #Checking if org name is correct
        try {
            if (-not [string]::IsNullOrWhiteSpace($env:RefreshToken) -and -not [string]::IsNullOrWhiteSpace($env:ClientSecret))
            {
                $apiURL = "https://app.vssps.visualstudio.com/_apis/accounts"
                $responseObj = "";

                $responseObj = [WebRequestHelper]::InvokeGetWebRequest($apiURL) ;
                if (-not [string]::IsNullOrEmpty($responseObj) -and ($responseObj | Measure-Object).Count -gt 0)
                {
                    $organizationId = ($responseObj | Where-Object {$_.accountname -eq $this.organizationname}).AccountId
                }
                Remove-Variable responseObj;
            }
            else
            {
                $apiURL = "https://dev.azure.com/{0}/_apis/Contribution/HierarchyQuery?api-version=5.0-preview.1" -f $($this.organizationName);

                $inputbody = "{'contributionIds':['ms.vss-features.my-organizations-data-provider'],'dataProviderContext':{'properties':{'sourcePage':{'url':'https://dev.azure.com/$($this.organizationName)','routeId':'ms.vss-tfs-web.suite-me-page-route','routeValues':{'view':'projects','controller':'ContributedPage','action':'Execute'}}}}}" | ConvertFrom-Json
                $responseObj = [WebRequestHelper]::InvokePostWebRequest($apiURL, $inputbody);
                $organizationId = ($responseObj[0].dataProviders."ms.vss-features.my-organizations-data-provider".organizations | Where-Object {$_.name -eq $this.organizationName}).id
                $inputbody = $null;
                Remove-Variable inputbody;
            }
        }
        catch {
            $user = [ContextHelper]::GetCurrentSessionUser();
            $this.PublishCustomMessage("Organization not found: Incorrect organization name or '$($user)' account does not have necessary permission to access the organization. Use -ResetCredentials parameter in command to login with another account. `n", [MessageType]::Warning);
            throw;
        }
        if ($this.ResourceTypeName -in ([ResourceTypeName]::Organization, [ResourceTypeName]::All, [ResourceTypeName]::Org_Project_User) -and ([string]::IsNullOrEmpty($this.serviceIds)) )
        {
            #First condition if 'includeAdminControls' switch is passed or user is admin(PCA).
            #Second condition if explicitly -rtn flag passed to org or Org_Project_User
            #Third condition if 'gads' contains only admin scan parame, then no need to ask for includeAdminControls switch
            if (-not $this.skipOrgUserControls) {
                if (($this.includeAdminControls -or $this.isAdminControlScan()))
                {
                    #Select Org/User by default...
                    $link = "https://dev.azure.com/$($this.organizationName)/_settings"
                    $this.AddSVTResource($this.organizationName, $null ,"ADO.Organization", "organization/$($organizationId)", $null, $link);
                }
                elseif ( ($this.ResourceTypeName -in ([ResourceTypeName]::Organization, [ResourceTypeName]::Org_Project_User)) -or ( $this.BuildNames.Count -eq 0 -and $this.ReleaseNames.Count -eq 0 -and $this.ServiceConnections.Count -eq 0 -and $this.AgentPools.Count -eq 0 -and $this.VariableGroups.Count -eq 0) ) {
                    $this.PublishCustomMessage("You have requested scan for organization controls. However, you do not have admin permission. Use '-IncludeAdminControls' if you'd still like to scan them. (Some controls may not scan correctly due to access issues.)", [MessageType]::Info);
                    $this.PublishCustomMessage("`r`n");
                }
            }
        }
        if (-not $this.skipOrgUserControls) {
            if ($this.ResourceTypeName -in ([ResourceTypeName]::User, [ResourceTypeName]::All, [ResourceTypeName]::Org_Project_User, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User)) {

                $link = "https://dev.azure.com/$($this.organizationName)/_settings/users"
                $this.AddSVTResource($this.organizationName, $null,"ADO.User", "organization/$($organizationId)/user", $null, $link);

            }
        }
        $topNQueryString = ""
        if ($this.MaxObjectsToScan -ne 0)
        {
            #Add this to QS only if $MaxObj is specified. If so, this will download only $maxObj configs.
            $topNQueryString = '&$top='+ $this.MaxObjectsToScan
        }
        #Get project resources
        if ($this.ProjectNames.Count -gt 0) {
            $this.PublishCustomMessage("Querying api for resources to be scanned. This may take a while...");

            $this.PublishCustomMessage("Getting project configurations...");
            #TODO: By default api return only 100 projects. Added $top=1000 to fetch first 1000 projects. If there are morethan 1000 projects, pagination is implemented to fetch them
            $apiURL = 'https://dev.azure.com/{0}/_apis/projects?$top=1000&api-version=6.0' -f $($this.OrganizationContext.OrganizationName);
            $responseObj = "";
            try {
                $responseObj = [WebRequestHelper]::InvokeGetWebRequest($apiURL) ;
            }
            catch {
                Write-Host 'Project not found: Incorrect project name or you do not have necessary permission to access the project.' -ForegroundColor Red
                throw;
            }
            if (([Helpers]::CheckMember($responseObj, "count") -and $responseObj[0].count -gt 0) -or (($responseObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($responseObj[0], "name")))
            {
                if($this.ProjectNames -eq "*")
                {
                    $projects = $responseObj
                }
                else {
                    $projects = $responseObj | Where-Object { $this.ProjectNames -contains $_.name }
                }

                $responseObj = $null;
                Remove-Variable responseObj;

                $nProj = $this.MaxObjectsToScan;
                if (!$projects) {
                    Write-Host 'No project found to perform the scan.' -ForegroundColor Red
                }
                $TotalSvc = 0;
                $ScannableSvc = 0;
                foreach ($thisProj in $projects)
                {
                    $projectName = $thisProj.name
                    $projectId = $thisProj.id;
                    [Hashtable] $projectData = @{
                        projectName = $projectName;
                        repositories = -1;
                        testPlan = -1;
                        build = -1;
                        release = -1;
                        taskGroups = -1;
                        agentPools = -1;
                        variableGroups = -1;
                        serviceConnections = -1;
                    };

                    if($this.UsePartialCommits -and $this.DoNotRefetchResources){
                        
                        [PartialScanManager] $partialScanMngr = [PartialScanManager]::GetInstance();
                        if(($partialScanMngr.IsPartialScanInProgress($this.OrganizationContext.OrganizationName, $false) -eq [ActiveStatus]::Yes)  ){
                            Write-Host "Resuming scan from last commit. Fetching unscanned resources..." -ForegroundColor Yellow
                            $this.nonScannedResources = $partialScanMngr.GetNonScannedResources();
                            $this.IsPartialScanActive=$true;
                        }
                        else {
                            $this.IsPartialScanActive=$false;
                        }
                    }

                    if($this.IsPartialScanActive -and $this.nonScannedResources.Count -ne 0){
                        #$this.PublishCustomMessage("Resuming scan from last commit")
                        if ($this.BuildNames.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::Build, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))) {
                            $this.createBuildSVTResource($projectName,$organizationId,$projectId);
                        }
                        if ($this.ServiceConnections.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::ServiceConnection, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))){
                            $this.createServiceConnectionSVTResource($projectName,$organizationId,$projectId,$projectData)
                        }
                        $progressCount=1



                        foreach($nonScannedResource in $this.nonScannedResources){
                            $nonScannedResourceType=$this.FindResourceTypeFromPartialScan($nonScannedResource.Id)
                            $nonScannedresourceLink=$this.CreateResourceLinkFromPartialScan($nonScannedResource.Id,$nonScannedResourceType,$this.organizationName,$projectName,$projectId)
                            if($nonScannedResourceType -eq "ADO.Project"){
                                $this.AddSVTResource($nonScannedResource.Name,$projectName,$nonScannedResourceType, $nonScannedResource.Id,$thisProj , $nonScannedresourceLink)
                            }
                            elseif($nonScannedResourceType -eq "ADO.Organization" -or $nonScannedResourceType -eq "ADO.User"){
                                $this.AddSVTResource($nonScannedResource.Name,$null,$nonScannedResourceType, $nonScannedResource.Id, $null, $nonScannedresourceLink)
                            }
                            elseif($nonScannedResourceType -eq "ADO.Release" -or $nonScannedResourceType -eq "ADO.AgentPool"){
                                $this.AddSVTResource($nonScannedResource.Name,$projectName,$nonScannedResourceType, $nonScannedResource.Id,$null , $nonScannedresourceLink)
                            }
                            
                            Write-Progress -Activity "Fetching $($progressCount) of $($this.nonScannedResources.Length) unscanned resources " -Status "Progress: " -PercentComplete ($progressCount / $this.nonScannedResources.Length * 100)
                            $progressCount++;
                            


                        }
                        Write-Progress -Activity "All resources fetched" -Status "Ready" -Completed

                    }

                    else{

                    if ($this.ResourceTypeName -in ([ResourceTypeName]::Project, [ResourceTypeName]::All, [ResourceTypeName]::Org_Project_User)  -and ([string]::IsNullOrEmpty($this.serviceIds)))
                    {
                        #First condition if 'includeAdminControls' switch is passed or user is PCA or User is PA.
                        #Second condition if explicitly -rtn flag passed to org or Org_Project_User
                        #Adding $this.isAdminControlScan() check in the end in case $this.isUserPCA is not checked (this happens when u scan using -svcid flag and org controls are not resolved/scanned)
                        if ( ($this.includeAdminControls -or $this.isUserPCA -or $this.isUserPA($projectName) -or $this.isAdminControlScan()))  {
                            $link = $thisProj.url.Replace('/_apis/projects', '') + '/_settings/'
                            $resourceId = "organization/$organizationId/project/$projectId"
                            $this.AddSVTResource($thisProj.name, $this.organizationName,"ADO.Project", $resourceId, $thisProj, $link);
                        }
                        #Third condition if 'gads' contains only admin scan parame, then no need to ask for includeAdminControls switch
                        elseif ( ($this.ResourceTypeName -in ([ResourceTypeName]::Project, [ResourceTypeName]::Org_Project_User)) -or ( $this.BuildNames.Count -eq 0 -and $this.ReleaseNames.Count -eq 0 -and $this.ServiceConnections.Count -eq 0 -and $this.AgentPools.Count -eq 0 -and $this.VariableGroups.Count -eq 0) ) {
                            $this.PublishCustomMessage("`r`n");
                            $this.PublishCustomMessage("You have requested scan for project controls. However, you do not have admin permission. Use '-IncludeAdminControls' if you'd still like to scan them. (Some controls may not scan correctly due to access issues.)", [MessageType]::Info);
                        }
                    }
                    #check if long running scan allowed or not.
                    if(!$this.isAllowLongRunningScanCheck())
                    {
                        return;
                    }

                    if($this.serviceIds.Count -gt 0) 
                    {
                        $inputBuildNames = @()
                        $inputReleaseNames = @()
                        $inputSvcNames = @()
                        $inputAgentPoolNames = @()
                        $inputVargrpNames = @()
                        $inputRepoNames = @()
                        $inputFeedNames = @()
                        $inputEnvNames = @()
                        $inputSecFileNames = @()

                        $inputBuildNames = $this.BuildNames;
                        $this.BuildNames =@();
   
                        $inputReleaseNames = $this.ReleaseNames;
                        $this.ReleaseNames =@();
                        
                        $inputSvcNames = $this.ServiceConnections;
                        $this.ServiceConnections =@();
                        
                        $inputAgentPoolNames = $this.AgentPools;
                        $this.AgentPools =@();
                        
                        $inputVargrpNames = $this.VariableGroups;
                        $this.VariableGroups =@();
                        
                        $inputRepoNames = $this.RepoNames;
                        $this.RepoNames  =@();
                        
                        $inputFeedNames = $this.FeedNames;
                        $this.FeedNames  =@();
                        
                        $inputEnvNames = $this.EnvironmentNames ;
                        $this.EnvironmentNames =@();
                            
                        $inputSecFileNames = $this.SecureFileNames ;
                        $this.SecureFileNames =@();             
                        
                        $this.PublishCustomMessage("Getting service associated resources...");                        
                        foreach ($thisServiceId in $this.serviceIds)
                        {                                                       
                            $this.FetchServiceAssociatedResources($thisServiceId, $projectName,$inputBuildNames,$inputReleaseNames,$inputSvcNames,$inputAgentPoolNames,$inputVargrpNames,$inputRepoNames,$inputFeedNames,$inputEnvNames,$inputSecFileNames);
                        }                      
                    }

                    if ($this.BuildNames.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::Build, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))) {
                        if ($this.ProjectNames -ne "*") {
                            $this.PublishCustomMessage("Getting build configurations...");
                        }

                        #When Undo fix for build/release inactive controls is called, resources need to be fetched from deleted list
                        if ($this.IsAutomatedFixUndoCmd)
                        {
                            $url = 'https://dev.azure.com/{0}/{1}/_build/deleted?__rt=fps&__ver=2 ' -f $($this.OrganizationContext.OrganizationName), $thisProj.name
                            $responseObj = @([WebRequestHelper]::InvokeGetWebRequest($url));
                            $buildDefnsObj = @()
                            if([Helpers]::CheckMember($responseObj,"fps.dataProviders.data") -and $responseObj.fps.dataProviders.data.'ms.vss-build-web.deleted-pipelines-data-provider' -and [Helpers]::CheckMember($responseObj.fps.dataProviders.data.'ms.vss-build-web.deleted-pipelines-data-provider',"pipelines") -and  $responseObj.fps.dataProviders.data.'ms.vss-build-web.deleted-pipelines-data-provider'.pipelines)
                            {
                                $buildDefnsObj = $responseObj.fps.dataProviders.data."ms.vss-build-web.deleted-pipelines-data-provider".pipelines;
                            }
                            if ($buildDefnsObj.count -gt 0) {
                                foreach ($bldDef in $buildDefnsObj) {
                                    $buildResourceId = "organization/$organizationId/project/$projectId/build/$($bldDef.id)";
                                    $this.AddSVTResource($bldDef.name, $thisProj.name, "ADO.Build", $buildResourceId, $bldDef, $null);
                                }
                                $buildDefnsObj = $null;
                                Remove-Variable buildDefnsObj;
                            }
                        }
                        else
                        {
                        if(-not [string]::IsNullOrEmpty($this.BuildsFolderPath)){
                            # Validate folder path is valid
                            $path = $this.BuildsFolderPath;
                            $this.BuildsFolderPath = $this.BuildsFolderPath.Replace(' ','%20').Replace('\','%5C')
                            $buildFoldersURL = "https://dev.azure.com/{0}/{1}/_apis/build/folders/{2}?api-version=6.1-preview.2"  -f $($this.OrganizationContext.OrganizationName), $thisProj.name, $this.BuildsFolderPath
                            $buildFoldersObj = [WebRequestHelper]::InvokeGetWebRequest($buildFoldersURL)
                            if($null -eq $buildFoldersObj -or (![Helpers]::CheckMember($buildFoldersObj[0],"Path"))){
                                $this.PublishCustomMessage("Folder path not found. Please validate the -BuildsFolderPath provided in the command. `n", [MessageType]::Warning);
                            }
                            else {

                                if($this.BatchScan){
                                    $this.addBuildsToSvtInBatchScan($projectName,$projectId,$path)
                                }
                                else {
                                #Iterate on each folder to get applicale build definition if folders count is le 100
                                if ([string]::IsNullOrEmpty($topNQueryString)) {
                                    $topNQueryString = '&$top=10000'
                                }
                                $nObj=$this.MaxObjectsToScan;
                                if($buildFoldersObj.Count -le 100)
                                {
                                    $folderCount=1
                                    
                                    foreach($path in $buildFoldersObj.Path)
                                    {
                                        
                                        $formattedPath = $path.Replace(' ','%20').Replace('\','%5C')
                                        $buildDefByFolderURL = ('https://dev.azure.com/{0}/{1}/_apis/build/definitions?path={2}&queryOrder=lastModifiedDescending'+$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $thisProj.name, $formattedPath
                                        Write-Progress -Activity "Searching in folder $($folderCount) of $($buildFoldersObj.Count) : $($path) " -Status "Progress: " -PercentComplete ($folderCount/ $buildFoldersObj.Count * 100)
                                        $this.addResourceToSVT($buildDefByFolderURL,"build",$projectName,$organizationId,$projectId,$true,$false,$null,[ref]$nObj)
                                        #if($nObj -eq 0) {break;}
                                        $folderCount++;
                                    }
                                    Write-Progress -Activity "All builds fetched" -Status "Ready" -Completed
                                }
                                else {                                 
                                    $buildDefURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?queryOrder=lastModifiedDescending&api-version=6.0" + $topNQueryString) -f $($this.OrganizationContext.OrganizationName), $thisProj.name;
                                    $this.addResourceToSVT($buildDefURL,"build",$projectName, $organizationId, $projectId, $true, $true, $path,[ref]$nObj)                                  
                                }
                            }

                            }
                        }
                        elseif ($this.BuildNames -eq "*") {
                            if($this.BatchScan){
                                $this.addBuildsToSvtInBatchScan($projectName,$projectId,$null);
                            }
                            else {
                                if ([string]::IsNullOrEmpty($topNQueryString)) {
                                    $topNQueryString = '&$top=10000'
                                    $buildDefnURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?queryOrder=lastModifiedDescending&api-version=6.0" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $thisProj.name;
                                }
                                else {
                                    $buildDefnURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?api-version=6.0" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $thisProj.name;
                                }
    
                                $nObj=$this.MaxObjectsToScan
                                $this.addResourceToSVT($buildDefnURL,"build",$projectName,$organizationId,$projectId,$false,$false,$null,[ref]$nObj);
    
                            }
                           

                            }
                        
                        else {
                            $nObj=$this.MaxObjectsToScan;
                            $buildDefnURL = "";
                            #If service id based scan then will break the loop after one run because, sending all build ids to api as comma separated in one go.
                            for ($i = 0; $i -lt $this.BuildNames.Count; $i++) {
                                #If service id based scan then send all build ids to api as comma separated in one go.
                                if ($this.isServiceIdBasedScan -eq $true) {
                                    $buildDefnURL = "https://{0}.visualstudio.com/{1}/_apis/build/definitions?definitionIds={2}&api-version=6.0" -f $($this.OrganizationContext.OrganizationName), $projectName, ($this.BuildIds -join ",");
                                }
                                else { #If normal scan (not service id based) then send each build name in api one by one.
                                    $buildDefnURL = "https://{0}.visualstudio.com/{1}/_apis/build/definitions?name={2}&api-version=6.0" -f $($this.OrganizationContext.OrganizationName), $projectName, $this.BuildNames[$i];
                                }
                                $buildDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($buildDefnURL)
                                if (([Helpers]::CheckMember($buildDefnsObj, "count") -and $buildDefnsObj[0].count -gt 0) -or (($buildDefnsObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($buildDefnsObj[0], "name"))) {
                                    foreach ($bldDef in $buildDefnsObj) {
                                        $link = $bldDef.url.split('?')[0].replace('_apis/build/Definitions/', '_build?definitionId=');
                                        $buildResourceId = "organization/$organizationId/project/$projectId/build/$($bldDef.id)";
                                        $this.AddSVTResource($bldDef.name, $bldDef.project.name, "ADO.Build", $buildResourceId, $bldDef, $link);
                                        if (--$nObj -eq 0) { break; }
                                    }
                                    $buildDefnsObj = $null;
                                    Remove-Variable buildDefnsObj;
                                }
                                #If service id based scan then no need to run loop as all the build ids has been sent to api as comma separated list in one go. so break the loop.
                                if ($this.isServiceIdBasedScan -eq $true) {
                                    break;
                                }
                            }
                        }
                        }

                        #Initialysing null to SecurityNamespaceId variable for new scan, it is static variable, setting once only in svc class and same value is applicable for all the svc con withing org
                        [Build]::SecurityNamespaceId = $null;

                    }
                    #check if long running scan allowed or not.
                    if(!$this.isAllowLongRunningScanCheck())
                    {
                        return;
                    }
                    if ($this.ReleaseNames.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::Release, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User)))
                    {
                        if ($this.ProjectNames -ne "*") {
                            $this.PublishCustomMessage("Getting release configurations...");
                        }
                        #When Undo fix for build/release inactive controls is called, resources need to be fetched from deleted list
                        if ($this.IsAutomatedFixUndoCmd)
                        {
                            $accessToken = [RemoteApiHelper]::GetAccessToken()
                            $apiURL = "https://vsrm.dev.azure.com/{0}/_apis/Contribution/HierarchyQuery/project/{1}" -f $($this.organizationName), $projectId
                            $inputbody = "{'contributionIds':['ms.vss-releaseManagement-web.deleted-definitions-data-provider'],'dataProviderContext':{'properties':{'sourcePage':{'url':'https://dev.azure.com/$($this.organizationName)/$projectName/_release?view=deleted','routeId':'ms.vss-releaseManagement-web.hub-explorer-3-default-route','routeValues':{'project':'$projectName','viewname':'hub-explorer-3-view','controller':'ContributedPage','action':'Execute'}}}}}" | ConvertFrom-Json
                            $headers = @{
                                "Authorization"= ("Bearer " + $accessToken); 
                                "Accept"="application/json;api-version=5.0-preview.1;excludeUrls=true;enumsAsNumbers=true;msDateFormat=true;noArrayWrap=true";
                                "content-type"="application/json";
                            };

                            $responseObj = [WebRequestHelper]::InvokePostWebRequest($apiURL,$headers, $inputbody);
                            $releaseDefnsObj = @()
                            if([Helpers]::CheckMember($responseObj,"dataProviders") -and $responseObj.dataProviders.'ms.vss-releaseManagement-web.deleted-definitions-data-provider' -and [Helpers]::CheckMember($responseObj.dataProviders.'ms.vss-releaseManagement-web.deleted-definitions-data-provider',"releaseDefinitions") -and  $responseObj.dataProviders.'ms.vss-releaseManagement-web.deleted-definitions-data-provider'.releaseDefinitions)
                            {
                                $releaseDefnsObj = $responseObj.dataProviders."ms.vss-releaseManagement-web.deleted-definitions-data-provider".releaseDefinitions;
                            }

                            if ($releaseDefnsObj.count -gt 0) {
                                foreach ($relDef in $releaseDefnsObj) {
                                    $releaseResourceId = "organization/$organizationId/project/$projectId/release/$($relDef.id)";
                                    $this.AddSVTResource($relDef.name, $projectName, "ADO.Release", $releaseResourceId, $relDef, $null);
                                }
                                $releaseDefnsObj = $null;
                                Remove-Variable releaseDefnsObj;
                            }
                        }
                        else
                        {

                        if(-not [string]::IsNullOrEmpty($this.ReleasesFolderPath)){
                            # Validate folder path is valid
                            $path = $this.ReleasesFolderPath;
                            $this.ReleasesFolderPath = $this.ReleasesFolderPath.Replace(' ','%20').Replace('\','%5C')
                            $releasesFoldersURL = "https://vsrm.dev.azure.com/{0}/{1}/_apis/release/folders/{2}?api-version=6.1-preview.2"  -f $($this.OrganizationContext.OrganizationName), $thisProj.name, $this.ReleasesFolderPath
                            $releasesFoldersObj = [WebRequestHelper]::InvokeGetWebRequest($releasesFoldersURL)
                            if($null -eq $releasesFoldersObj -or (![Helpers]::CheckMember($releasesFoldersObj[0],"Path"))){
                                $this.PublishCustomMessage("Folder path not found. Please validate the -ReleasesFolderPath provided in the command. `n", [MessageType]::Warning);
                            }
                            else {
                                if($this.BatchScan){
                                    $this.addReleasesToSvtInBatchScan($projectName,$projectId,$path)
                                }
                                else {
                               #API doesnt provide all folders in a path, fallback to fetch all resources and then filter
                                $nObj=$this.MaxObjectsToScan                                                               
                                $releaseDefURL = ("https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?api-version=6.0" ) -f $($this.OrganizationContext.OrganizationName), $thisProj.name;
                                $this.addResourceToSVT($releaseDefURL,"release",$projectName, $organizationId, $projectId, $true, $true, $path,[ref]$nObj)   
                                }                               
                                

                            }
                           
                            
                            
                            
                        }


                        elseif ($this.ReleaseNames -eq "*")
                        {
                            if($this.BatchScan){
                                $this.addReleasesToSvtInBatchScan($projectName,$projectId,$null);
                            }
                            else {
                            $nObj=$this.MaxObjectsToScan
                            $releaseDefnURL = ("https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?api-version=6.0") -f $($this.OrganizationContext.OrganizationName), $projectName;
                            $this.addResourceToSVT($releaseDefnURL,"release",$projectName,$organizationId,$projectId,$false,$false,$null,[ref]$nObj);
                            }
                        }
                        else {
                            try {
                                $nObj=$this.MaxObjectsToScan
                                $releaseDefnsObj = $null;
                                #If service id based scan then will break the loop after one run because, sending all release ids to api as comma separated in one go.
                                for ($i = 0; $i -lt $this.ReleaseNames.Count; $i++) {
                                    #If service id based scan then send all release ids to api as comma separated in one go.
                                    if ($this.isServiceIdBasedScan -eq $true) {
                                        $url = "https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?definitionIdFilter={2}&api-version=6.0" -f $($this.OrganizationContext.OrganizationName), $projectName, ($this.ReleaseIds -join ",");
                                    }
                                    else { #If normal scan (not service id based) then send each release name in api one by one.
                                        $url = "https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?searchText={2}&isExactNameMatch=true&api-version=6.0" -f $($this.OrganizationContext.OrganizationName), $projectName, $this.ReleaseNames[$i];
                                    }
                                    $releaseDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($url);

                                    foreach ($relDef in $releaseDefnsObj) {
                                        $link = "https://dev.azure.com/{0}/{1}/_release?_a=releases&view=mine&definitionId={2}" -f $this.OrganizationContext.OrganizationName, $projectName, $relDef.url.split('/')[-1];
                                        $releaseResourceId = "organization/$organizationId/project/$projectId/release/$($relDef.id)";
                                        $this.AddSVTResource($relDef.name, $projectName, "ADO.Release", $releaseResourceId, $null, $link);
                                        if (--$nObj -eq 0) { break; }
                                    }
                                    #If service id based scan then no need to run loop as all the release ids has been sent to api as comma separated list in one go. so break the loop.
                                    if ($this.isServiceIdBasedScan -eq $true) {
                                        break;
                                    }
                                }
                            }
                            catch {
                                #Write-Error $_.Exception.Message;
                                Write-Warning "Release pipelines for the project [$($projectName)] could not be fetched.";
                            }
                        }
                        }

                        #Initialysing null to SecurityNamespaceId variable for new scan, it is static variable, setting once only in release class and same value is applicable for all the release pipelines withing org
                        [Release]::SecurityNamespaceId = $null;

                    }
                    #check if long running scan allowed or not.
                    if(!$this.isAllowLongRunningScanCheck())
                    {
                        return;
                    }

                    #Note: $topNQueryString is currently not supported in the SvcConn and AgentPool APIs.
                    if ($this.ServiceConnections.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::ServiceConnection, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources)))
                    {
                        if ($this.ProjectNames -ne "*") {
                            $this.PublishCustomMessage("Getting service endpoint configurations...");
                        }

                        # Here we are fetching all the svc conns in the project and then filtering out. But in build & release we fetch them individually unless '*' is used for fetching all of them.
                        $serviceEndpointURL = ("https://dev.azure.com/{0}/{1}/_apis/serviceendpoint/endpoints?includeDetails=True&api-version=6.0-preview.4") -f $($this.organizationName), $($projectName);
                        $serviceEndpointObj = [WebRequestHelper]::InvokeGetWebRequest($serviceEndpointURL)
                        $TotalSvc += ($serviceEndpointObj | Measure-Object).Count
                        # service connection count here
                        $projectData["serviceConnections"] = ($serviceEndpointObj | Measure-Object).Count;

                        if (([Helpers]::CheckMember($serviceEndpointObj, "count") -and $serviceEndpointObj[0].count -gt 0) -or (($serviceEndpointObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($serviceEndpointObj[0], "name"))) {
                            # Currently get only Azure Connections as all controls are applicable for same

                            $Connections = $null;
                            if ($this.ServiceConnections -eq "*") {
                                $Connections = $serviceEndpointObj #| Where-Object { ($_.type -eq "azurerm" -or $_.type -eq "azure" -or $_.type -eq "git" -or $_.type -eq "github" -or $_.type -eq "externaltfs" -or $_.type -eq "externalnpmregistry" -or $_.type -eq "generic" -or $_.type -eq "externalnugetfeed" -or $_.type -eq "PRSS" -or $_.type -eq "ESRPScan") }
                            }
                            else {
                                #If service id based scan then filter with serviceconnection ids
                                if ($this.isServiceIdBasedScan -eq $true) {
                                    $Connections = $serviceEndpointObj | Where-Object {  ($this.ServiceConnectionIds -eq $_.Id) }  # ($_.type -eq "azurerm" -or $_.type -eq "azure" -or $_.type -eq "git" -or $_.type -eq "github" -or $_.type -eq "externaltfs" -or $_.type -eq "externalnpmregistry" -or $_.type -eq "generic" -or $_.type -eq "externalnugetfeed" -or $_.type -eq "PRSS" -or $_.type -eq "ESRPScan") -and
                                }
                                else {
                                    $Connections = $serviceEndpointObj | Where-Object {  ($this.ServiceConnections -eq $_.name) }  # ($_.type -eq "azurerm" -or $_.type -eq "azure" -or $_.type -eq "git" -or $_.type -eq "github" -or $_.type -eq "externaltfs" -or $_.type -eq "externalnpmregistry" -or $_.type -eq "generic" -or $_.type -eq "externalnugetfeed" -or $_.type -eq "PRSS" -or $_.type -eq "ESRPScan" -or $_.type -eq "servicefabric") -and
                                }
                            }
                            $ScannableSvc += ($connections | Measure-Object).Count

                            #Initialising null to SecurityNamespaceId variable for new scan, it is static variable, setting once only in svc class and same value is applicable for all the svc con withing org
                            [ServiceConnection]::SecurityNamespaceId = $null;
                            $serviceEndpointObj = $null;
                            Remove-Variable  serviceEndpointObj;
                            $nObj = $this.MaxObjectsToScan
                            foreach ($connectionObject in $Connections) {
                                $resourceId = "organization/$organizationId/project/$projectId/serviceconnection/$($connectionObject.Id)";
                                $link = "https://dev.azure.com/$($this.organizationName)/$projectId/_settings/adminservices?resourceId=$($connectionObject.Id)";
                                $this.AddSVTResource($connectionObject.name, $projectName, "ADO.ServiceConnection", $resourceId, $connectionObject, $link);

                                if (--$nObj -eq 0) { break; }
                            }
                        }
                    }
                    #check if long running scan allowed or not.
                    if(!$this.isAllowLongRunningScanCheck())
                    {
                        return;
                    }
                    if ($this.AgentPools.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::AgentPool, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources)))
                    {
                        if ($this.ProjectNames -ne "*") {
                            $this.PublishCustomMessage("Getting agent pools configurations...");
                        }
                        # Here we are fetching all the agent pools in the project and then filtering out. But in build & release we fetch them individually unless '*' is used for fetching all of them.
                        if (-not [string]::IsNullOrWhiteSpace($env:RefreshToken) -and -not [string]::IsNullOrWhiteSpace($env:ClientSecret))
                        {
                            $agentPoolsDefnURL =  "https://dev.azure.com/{0}/{1}/_apis/distributedtask/queues?api-version=6.1-preview.1" -f $($this.OrganizationContext.OrganizationName), $projectName;

                            try {
                                $agentPoolsDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($agentPoolsDefnURL);

                                if (($agentPoolsDefnsObj | Measure-Object).Count -gt 0 ) {
                                    $nObj = $this.MaxObjectsToScan

                                    $projectData["agentPools"] = ($agentPoolsDefnsObj | Measure-Object).Count

                                    if ($this.AgentPools -eq "*") {
                                        # We need to filter out legacy agent pools (Hosted, Hosted VS 2017 etc.) as they are not visible to user on the portal. As a result, they won't be able to remediate their respective controls
                                        $taskAgentQueues = $agentPoolsDefnsObj | where-object{$_.pool.isLegacy -eq $false};
                                    }
                                    else {
                                        #If service id based scan then filter with agent pool ids
                                        if ($this.isServiceIdBasedScan -eq $true) {
                                            $taskAgentQueues = $agentPoolsDefnsObj | Where-Object {($_.pool.isLegacy -eq $false) -and ($this.AgentPoolIds -contains $_.Id) }
                                        }
                                        else {
                                            $taskAgentQueues = $agentPoolsDefnsObj | Where-Object {($_.pool.isLegacy -eq $false) -and ($this.AgentPools -contains $_.name) }
                                        }
                                    }
                                    #Filtering out "Azure Pipelines" agent pool from scan as it is created by ADO by default and some of its settings are not editable (grant access to all pipelines, auto-provisioning etc.)
                                    $taskAgentQueues = $taskAgentQueues | where-object{$_.name -ne "Azure Pipelines"};

                                    foreach ($taq in $taskAgentQueues) {
                                        $resourceId = "https://dev.azure.com/{0}/_apis/securityroles/scopes/distributedtask.agentqueuerole/roleassignments/resources/{1}_{2}" -f $($this.OrganizationContext.OrganizationName), $($taq.projectId), $taq.id
                                        $agtpoolResourceId = "organization/$organizationId/project/$projectId/agentpool/$($taq.id)";
                                        $link = "https://dev.azure.com/{0}/{1}/_settings/agentqueues?queueId={2}&view=security" -f $($this.OrganizationContext.OrganizationName), $($taq.projectId), $taq.id
                                        $this.AddSVTResource($taq.name, $projectName, "ADO.AgentPool", $agtpoolResourceId, $null, $link);

                                        if (--$nObj -eq 0) { break; }
                                    }
                                    $taskAgentQueues = $null;
                                    Remove-Variable taskAgentQueues;
                                }
                            }
                            catch {
                                Write-Warning "Agent pools for the project [$($projectName)] could not be fetched.";
                            }
                        }
                        else {
                            $agentPoolsDefnURL = ("https://dev.azure.com/{0}/{1}/_settings/agentqueues?__rt=fps&__ver=2") -f $($this.OrganizationContext.OrganizationName), $projectName;
                            try {

                                $agentPoolsDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($agentPoolsDefnURL);

                                #Here the return obj for agent pool is different than prj, build, release & svc conns. Also, Azure Pipelines agent pool will always be a part of org and project. We can't delete it.
                                if (([Helpers]::CheckMember($agentPoolsDefnsObj, "fps.dataProviders.data") ) -and (($agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider") -and $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues)) {
                                    $nObj = $this.MaxObjectsToScan
                                    $taskAgentQueues = $null;
                                    if(($agentPoolsDefnsObj | Measure-Object).Count -gt 0) {
                                        $allAgentPools = $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues;
                                        $projectData["agentPools"] = ($allAgentPools | Measure-Object).Count
                                    }
                                    if ($this.AgentPools -eq "*") {
                                        # We need to filter out legacy agent pools (Hosted, Hosted VS 2017 etc.) as they are not visible to user on the portal. As a result, they won't be able to remediate their respective controls
                                        $taskAgentQueues = $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues | where-object{$_.pool.isLegacy -eq $false};
                                    }
                                    else {
                                        #If service id based scan then filter with agent pool ids
                                        if ($this.isServiceIdBasedScan -eq $true) {
                                            $taskAgentQueues = $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues | Where-Object {($_.pool.isLegacy -eq $false) -and ($this.AgentPoolIds -contains $_.Id) }
                                        }
                                        else {
                                            $taskAgentQueues = $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues | Where-Object {($_.pool.isLegacy -eq $false) -and ($this.AgentPools -contains $_.name) }
                                        }
                                    }
                                    #Filtering out "Azure Pipelines" agent pool from scan as it is created by ADO by default and some of its settings are not editable (grant access to all pipelines, auto-provisioning etc.)
                                    $taskAgentQueues = $taskAgentQueues | where-object{$_.name -ne "Azure Pipelines"};

                                    foreach ($taq in $taskAgentQueues) {
                                        $resourceId = "https://dev.azure.com/{0}/_apis/securityroles/scopes/distributedtask.agentqueuerole/roleassignments/resources/{1}_{2}" -f $($this.OrganizationContext.OrganizationName), $($taq.projectId), $taq.id
                                        $agtpoolResourceId = "organization/$organizationId/project/$projectId/agentpool/$($taq.id)";
                                        $link = "https://dev.azure.com/{0}/{1}/_settings/agentqueues?queueId={2}&view=security" -f $($this.OrganizationContext.OrganizationName), $($taq.projectId), $taq.id
                                        $this.AddSVTResource($taq.name, $projectName, "ADO.AgentPool", $agtpoolResourceId, $null, $link);

                                        if (--$nObj -eq 0) { break; }
                                    }
                                    $taskAgentQueues = $null;
                                    Remove-Variable taskAgentQueues;
                                }
                            }
                            catch {
                                Write-Warning "Agent pools for the project [$($projectName)] could not be fetched.";
                            }
                        }
                    }
                    #check if long running scan allowed or not.
                    if(!$this.isAllowLongRunningScanCheck())
                    {
                        return;
                    }
                    if ($this.VariableGroups.Count -gt 0 -and ($this.ResourceTypeName -in ([ResourceTypeName]::VariableGroup, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources)))
                    {
                        if ($this.ProjectNames -ne "*") {
                            $this.PublishCustomMessage("Getting variable group configurations...");
                        }
                        try
                        {
                            if ($this.VariableGroups -eq "*") {
                                $variableGroupURL = ("https://dev.azure.com/{0}/{1}/_apis/distributedtask/variablegroups?api-version=6.1-preview.2" +$topNQueryString) -f $($this.organizationName), $projectId;
                                $variableGroupObj = [WebRequestHelper]::InvokeGetWebRequest($variableGroupURL)

                                if (([Helpers]::CheckMember($variableGroupObj, "count") -and $variableGroupObj[0].count -gt 0) -or (($variableGroupObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($variableGroupObj[0], "name"))) 
                                {
                                    foreach ($group in $variableGroupObj) {
                                        $resourceId = "organization/$organizationId/project/$projectId/variablegroup/$($group.Id)";
                                        $link = ("https://dev.azure.com/{0}/{1}/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId={2}") -f $($this.organizationName), $projectName, $($group.Id);
                                        $this.AddSVTResource($group.name, $projectName, "ADO.VariableGroup", $resourceId, $group, $link);
                                    }
                                    $variableGroupObj = $null
                                }
                            }
                            else {
                                for ($i = 0; $i -lt $this.VariableGroups.Count; $i++) {
                                    # This API does not support multiple variable group names at one go.
                                    $variableGroupURL = ("https://dev.azure.com/{0}/{1}/_apis/distributedtask/variablegroups?groupName={2}&api-version=6.0-preview.2") -f $($this.organizationName), $projectId, $this.VariableGroups[$i];
                                    $variableGroupObj = [WebRequestHelper]::InvokeGetWebRequest($variableGroupURL)

                                    if (([Helpers]::CheckMember($variableGroupObj, "count") -and $variableGroupObj[0].count -gt 0) -or (($variableGroupObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($variableGroupObj[0], "name"))) 
                                    {
                                        $varGroup = $null;
                                        #If service id based scan then filter with variablegroup ids
                                        if ($this.isServiceIdBasedScan -eq $true) {
                                            $varGroup = $variableGroupObj | Where-Object { $this.VariableGroupIds -eq $_.Id }
                                        }
                                        else {
                                            $varGroup = $variableGroupObj | Where-Object { $this.VariableGroups -eq $_.name }
                                        }
                                        foreach ($group in $varGroup) {
                                            $resourceId = "organization/$organizationId/project/$projectId/variablegroup/$($group.Id)";
                                            $link = ("https://dev.azure.com/{0}/{1}/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId={2}") -f $($this.organizationName), $projectName, $($group.Id);
                                            $this.AddSVTResource($group.name, $projectName, "ADO.VariableGroup", $resourceId, $group, $link);
                                        }
                                    }
                                }
                            }
                        }
                        catch {
                            Write-Warning "Variable groups for the project [$($projectName)] could not be fetched.";
                        }
                        
                    }

                    #Creating resource in common resource resolver
                    if ($this.RepoNames.count -gt 0 -or $this.SecureFileNames.count -gt 0 -or $this.FeedNames.count -gt 0 -or $this.EnvironmentNames.count -gt 0 -or ($this.ResourceTypeName -in ([ResourceTypeName]::Repository, [ResourceTypeName]::SecureFile, [ResourceTypeName]::Feed, [ResourceTypeName]::Environment, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources))) {
                        $commonSVTResourceResolverObj = [CommonSVTResourceResolver]::new($this.organizationName, $organizationId, $projectId);
                        $this.SVTResources += $commonSVTResourceResolverObj.LoadResourcesForScan($projectName, $this.RepoNames, $this.SecureFileNames, $this.FeedNames, $this.EnvironmentNames, $this.ResourceTypeName, $this.MaxObjectsToScan, $this.isServiceIdBasedScan);
                    }

                    #Fetch only those resources for which data obj backup is available in local
                    if([ControlHelper]::ControlFixBackup.Count -gt 0)
                    {
                        $this.SVTResources = @($this.SVTResources | Where-Object {[ControlHelper]::ControlFixBackup.ResourceId -contains $_.ResourceId})
                        if ($this.ResourceNames.count -gt 0) {
                            $this.SVTResources = @($this.SVTResources | Where-Object {$this.ResourceNames -contains $_.ResourceName})
                        }
                        if ($this.ExcludeResourceNames.count -gt 0) {
                            $this.SVTResources = @($this.SVTResources | Where-Object {$this.ExcludeResourceNames -notcontains $_.ResourceName})
                        }
                    }

                    # getting all the resources count
                    # and sending them to telemetry as well
                    $scanSource = [AzSKSettings]::GetInstance().GetScanSource(); # Disabling resource telemetry for SDL scan.
                    if($this.IsAIEnabled -eq $true -and $scanSource -ne 'SDL') {
                        [InventoryHelper]::GetResourceCount($this.organizationName, $projectName, $projectId, $projectData);
                    }
                    #check if long running scan allowed or not.
                    if(!$this.isAllowLongRunningScanCheck())
                    {
                        return;
                    }
                    if (--$nProj -eq 0) { break; } #nProj is set to MaxObj before loop.
                }
                    

            }
                
                #Display count of total svc and svcs to be scanned
                #sending the details to telemetry as well
                if ($TotalSvc -gt 0)
                {
                    #$this.PublishCustomMessage("Total service connections: $TotalSvc");
                    #$this.PublishCustomMessage("Total service connections that will be scanned: $ScannableSvc");

                    $properties =  @{
                        "TotalServiceConnections" = $TotalSvc;
                        "ScannableServiceConnections" = $ScannableSvc;
                    }
                    [AIOrgTelemetryHelper]::PublishEvent( "Service Connections count",$properties, @{})
                }
            }
        }
        $this.SVTResourcesFoundCount = $this.SVTResources.Count
    }

    [bool] isAllowLongRunningScanCheck()
    {
        if ($this.SVTResources.count -gt $this.longRunningScanCheckPoint)
        {
            if (!$this.isAllowLongRunningScanInPolicy) {
                Write-Host ([Constants]::LongRunningScanStopByPolicyMsg) -ForegroundColor Yellow;
                $this.SVTResources = $null
                return $false;
            }
            elseif(!$this.allowLongRunningScan)
            {
                Write-Host ([Constants]::LongRunningScanStopMsg -f $this.longRunningScanCheckPoint) -ForegroundColor Yellow;
                $this.SVTResources = $null
                return $false;
            }
        }
        return $true;
    }

    [void] AddSVTResource([string] $name, [string] $resourceGroupName, [string] $resourceType, [string] $resourceId, [PSObject] $resourceDetailsObj, $resourceLink)
    {
        $svtResource = [SVTResource]::new();
        $svtResource.ResourceName = $name;
        if ($resourceGroupName) {
            $svtResource.ResourceGroupName = $resourceGroupName;
        }
        $svtResource.ResourceType = $resourceType;
        $svtResource.ResourceId = $resourceId;
        $svtResource.ResourceTypeMapping = ([SVTMapping]::AzSKADOResourceMapping | Where-Object { $_.ResourceType -eq $resourceType } | Select-Object -First 1)

        if ($resourceDetailsObj) {
            $svtResource.ResourceDetails = $resourceDetailsObj;
            if(![Helpers]::CheckMember($svtResource.ResourceDetails,'ResourceLink')){
            $svtResource.ResourceDetails | Add-Member -Name 'ResourceLink' -Type NoteProperty -Value $resourceLink;
            }
        }
        else {
            $svtResource.ResourceDetails = New-Object -TypeName psobject -Property @{ ResourceLink = $resourceLink }
        }

        $this.SVTResources += $svtResource
    }

    [void] FetchServiceAssociatedResources($svcId, $projectName,$inputBuildNames,$inputReleaseNames,$inputSvcNames,$inputAgentPoolNames,$inputVargrpNames,$inputRepoNames,$inputFeedNames,$inputEnvNames,$inputSecFileNames)
    {        
        $metaInfo = [MetaInfoProvider]::Instance;

        $rsrcList = $metaInfo.FetchServiceAssociatedResources($svcId, $projectName, $this.ResourceTypeName);
        $bFoundSvcMappedObjects = $false
        if ($null -ne $rsrcList)
        {
            $this.isServiceIdBasedScan = $true;
            if ($this.ResourceTypeName -in ([ResourceTypeName]::Build, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))
            {
                if ($rsrcList.Builds -and $rsrcList.Builds.Count -gt 0)
                {
                    if ($inputBuildNames -ne "*") {
                        $rsrcList.Builds = @($rsrcList.Builds | Where { $_.buildDefinitionName -in $inputBuildNames });
                    }
                    if ($rsrcList.Builds -and $rsrcList.Builds.Count -gt 0) {
                        $this.BuildNames += $rsrcList.Builds.buildDefinitionName
                        $this.BuildIds += $rsrcList.Builds.buildDefinitionId
                        $bFoundSvcMappedObjects = $true
                    }                   
                }
            }
            if ($this.ResourceTypeName -in ([ResourceTypeName]::Release, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))
            {
                if ($rsrcList.Releases -and $rsrcList.Releases.Count -gt 0)
                {
                    if ($inputReleaseNames -ne "*") {
                        $rsrcList.Releases = @($rsrcList.Releases | Where { $_.releaseDefinitionName -in $inputReleaseNames });
                    }
                    if ($rsrcList.Releases -and $rsrcList.Releases.Count -gt 0) {
                        $this.ReleaseNames += $rsrcList.Releases.releaseDefinitionName
                        $this.ReleaseIds += $rsrcList.Releases.releaseDefinitionId
                        $bFoundSvcMappedObjects = $true
                    }                 
                }
            }
            if ($this.ResourceTypeName -in ([ResourceTypeName]::ServiceConnection, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))
            {
                if ($rsrcList.ServiceConnections -and $rsrcList.ServiceConnections.Count -gt 0)
                {
                    if ($inputSvcNames-ne "*") {
                        $rsrcList.ServiceConnections = @($rsrcList.ServiceConnections | Where { $_.serviceConnectionName -in $inputSvcNames }); 
                    }
                    if ($rsrcList.ServiceConnections -and $rsrcList.ServiceConnections.Count -gt 0) {
                        $this.ServiceConnections += $rsrcList.ServiceConnections.serviceConnectionName
                        $this.ServiceConnectionIds += $rsrcList.ServiceConnections.ServiceConnectionId
                        $bFoundSvcMappedObjects = $true
                    }                  
                }
            }
            if ($this.ResourceTypeName -in ([ResourceTypeName]::AgentPool, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))
            {
                if ($rsrcList.AgentPools -and $rsrcList.AgentPools.Count -gt 0)
                {
                    if ($inputAgentPoolNames -ne "*") {
                        $rsrcList.AgentPools = @($rsrcList.AgentPools | Where { $_.agentPoolName -in $inputAgentPoolNames }); 
                    }
                    if ($rsrcList.AgentPools -and $rsrcList.AgentPools.Count -gt 0) {
                        $this.AgentPools += $rsrcList.AgentPools.agentPoolName
                        $this.AgentPoolIds += $rsrcList.AgentPools.agentPoolId
                        $bFoundSvcMappedObjects = $true
                    }                 
                }
            }
            if ($this.ResourceTypeName -in ([ResourceTypeName]::VariableGroup, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User))
            {
                if ($rsrcList.VariableGroups -and $rsrcList.VariableGroups.Count -gt 0)
                {
                    if ($inputVargrpNames -ne "*") {
                        $rsrcList.VariableGroups = @($rsrcList.VariableGroups | Where { $_.variableGroupName -in $inputVargrpNames }); 
                    }
                    if ($rsrcList.VariableGroups -and $rsrcList.VariableGroups.Count -gt 0) {
                        $this.VariableGroups += $rsrcList.VariableGroups.variableGroupName
                        $this.VariableGroupIds += $rsrcList.VariableGroups.variableGroupId
                        $bFoundSvcMappedObjects = $true
                    }                 
                }
            }
            #TODO: Remove this try catch in 2110
            try
            {
                if ($this.ResourceTypeName -in ([ResourceTypeName]::Repository, [ResourceTypeName]::All))
                {
                    if ($rsrcList.Repositories -and $rsrcList.Repositories.Count -gt 0)
                    {
                        if ($inputRepoNames -ne "*") {
                            $rsrcList.Repositories = @($rsrcList.repositories | Where { $_.repoName -in $inputRepoNames }); 
                        }
                        if ($rsrcList.Repositories -and $rsrcList.Repositories.Count -gt 0) {
                            $this.RepoNames += $rsrcList.Repositories.repoName
                            $bFoundSvcMappedObjects = $true
                        }                      
                    }
                }
                if ($this.ResourceTypeName -in ([ResourceTypeName]::Feed, [ResourceTypeName]::All))
                {
                    if ($rsrcList.Feeds -and $rsrcList.Feeds.Count -gt 0)
                    {
                        if ($inputFeedNames -ne "*") {
                            $rsrcList.Feeds = @($rsrcList.Feeds | Where { $_.feedName -in $inputFeedNames }); 
                        }
                        if ($rsrcList.Feeds -and $rsrcList.Feeds.Count -gt 0) {
                            $this.FeedNames += $rsrcList.Feeds.feedName
                            $bFoundSvcMappedObjects = $true
                        }                     
                    }
                }
                if ($this.ResourceTypeName -in ([ResourceTypeName]::SecureFile, [ResourceTypeName]::All))
                {
                    if ($rsrcList.SecureFiles -and $rsrcList.SecureFiles.Count -gt 0)
                    {
                        if ($inputSecFileNames -ne "*") {
                            $rsrcList.SecureFiles = @($rsrcList.SecureFiles | Where { $_.secureFileName -in $inputSecFileNames }); 
                        }
                        if ($rsrcList.SecureFiles -and $rsrcList.SecureFiles.Count -gt 0) {
                            $this.SecureFileNames += $rsrcList.SecureFiles.secureFileName
                            $bFoundSvcMappedObjects = $true
                        }                    
                    }
                }
                if ($this.ResourceTypeName -in ([ResourceTypeName]::Environment, [ResourceTypeName]::All))
                {
                    if ($rsrcList.Environments -and $rsrcList.Environments.Count -gt 0)
                    {
                        if ($inputEnvNames -ne "*") {
                            $rsrcList.Environments = @($rsrcList.Environments | Where { $_.environmentName -in $inputEnvNames }); 
                        }
                        if ($rsrcList.Environments -and $rsrcList.Environments.Count -gt 0) {
                            $this.EnvironmentNames += $rsrcList.Environments.environmentName
                            $bFoundSvcMappedObjects = $true
                        }                  
                    }
                }
            }
            catch{
                #eat the exception
            }
        }
        if ($bFoundSvcMappedObjects -eq $false)
        {
            $this.PublishCustomMessage("Could not find any objects mapped to the provided service id : $svcId", [MessageType]::Warning);
        }
    }
    #check for PCA group members
    [bool] isAdminControlScan()
    {
        $allowedAdminGrp = $null;
        if (!$this.ControlSettings) {
            $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json");
        }
        if ([Helpers]::CheckMember($this.ControlSettings, "AllowAdminControlScanForGroups")) {
            $allowedAdminGrp = $this.ControlSettings.AllowAdminControlScanForGroups | where { $_.ResourceType -eq "Organization" } | select-object -property GroupNames
        }
        $this.isUserPCA = [AdministratorHelper]::isUserOrgAdminMember($this.organizationName, $allowedAdminGrp);
        return $this.isUserPCA;
    }

    #check for PA group members
    [bool] isUserPA($project)
    {
        $allowedAdminGrp = $null;
        if (!$this.ControlSettings) {
            $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json");
        }
        if ([Helpers]::CheckMember($this.ControlSettings, "AllowAdminControlScanForGroups")) {
            $allowedAdminGrp = $this.ControlSettings.AllowAdminControlScanForGroups | where { $_.ResourceType -eq "Project" } | select-object -property GroupNames
        }

        return [AdministratorHelper]::isUserProjectAdminMember($this.organizationName, $project, $allowedAdminGrp);
    }

    # getting resources count and sending them to telemetry as well
    [void] GetResourceCount($projectName, $organizationId, $projectId, $projectData) {
        try{
            # fetching the repository count of a project
            $resourceURL = "https://dev.azure.com/$($this.organizationName)/$($projectName)/_apis/git/repositories?api-version=6.1-preview.1"
            $responseList = [WebRequestHelper]::InvokeGetWebRequest($resourceURL) ;
            $projectData['repositories'] = ($responseList | Measure-Object).Count

            # fetching the testPlan count of a project
            $resourceURL = "https://dev.azure.com/$($this.organizationName)/$($projectName)/_apis/testplan/plans?api-version=6.0-preview.1"
            $responseList = [WebRequestHelper]::InvokeGetWebRequest($resourceURL) ;
            $projectData['testPlan'] = ($responseList | Measure-Object).Count

            # fetching the taskGroups count of a project
            $resourceURL = "https://dev.azure.com/$($this.organizationName)/$($projectName)/_apis/distributedtask/taskgroups?api-version=6.0-preview.1"
            $responseList = [WebRequestHelper]::InvokeGetWebRequest($resourceURL) ;
            $projectData['taskGroups'] = ($responseList | Measure-Object).Count

            # fetch the builds count
            $resourceURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?api-version=6.0&queryOrder=lastModifiedDescending&`$top=10000") -f $($this.OrganizationContext.OrganizationName), $projectName;
            $responseList = [WebRequestHelper]::InvokeGetWebRequest($resourceURL);
            $projectData['build'] = ($responseList | Measure-Object).Count

            # fetch the release count
            $resourceURL = ("https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?api-version=6.0&`$top=10000") -f $($this.OrganizationContext.OrganizationName), $projectName;
            $responseList = [WebRequestHelper]::InvokeGetWebRequest($resourceURL);
            $projectData['release'] = ($responseList | Measure-Object).Count;

            # fetch the agent pools count
            if($projectData["agentPools"] -eq -1) {
                $agentPoolsDefnURL = ("https://dev.azure.com/{0}/{1}/_settings/agentqueues?__rt=fps&__ver=2") -f $($this.OrganizationContext.OrganizationName), $projectName;
                $agentPoolsDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($agentPoolsDefnURL);
                if (([Helpers]::CheckMember($agentPoolsDefnsObj, "fps.dataProviders.data") ) -and (($agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider") -and $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues)) {
                    $taskAgentQueues = $agentPoolsDefnsObj.fps.dataProviders.data."ms.vss-build-web.agent-queues-data-provider".taskAgentQueues;
                    $projectData["agentPools"] = ($taskAgentQueues | Measure-Object).Count
                }
            }

            # fetch the variable groups count
            if ($projectData["variableGroups"] -eq -1) {
                $variableGroupURL = ("https://dev.azure.com/{0}/{1}/_apis/distributedtask/variablegroups?api-version=6.1-preview.2") -f $($this.organizationName), $projectId;
                $variableGroupObj = [WebRequestHelper]::InvokeGetWebRequest($variableGroupURL)
                if (([Helpers]::CheckMember($variableGroupObj, "count") -and $variableGroupObj[0].count -gt 0) -or (($variableGroupObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($variableGroupObj[0], "name"))) {
                    $varGroups = $variableGroupObj
                    $projectData["variableGroups"] = ($varGroups | Measure-Object).Count
                }
            }
        }
        catch {}
        [AIOrgTelemetryHelper]::PublishEvent("Projects resources count", $projectData, @{})
    }


    #only for build and release
    [void] addResourceToSVT([string] $resourceDfnUrl, [string] $resourceType, [string] $projectName, [string] $organizationId, [string]$projectId,  [bool]  $isFolderPathGiven, [bool] $isFolderSizegt100,[string] $path,[ref] $nObj){
        [System.Uri] $validatedUri = $null;
        $orginalUri = "";
        
        $skipCount = 0
        $batchCount = 1;
        #$nObj = $this.MaxObjectsToScan
        $timestamp = (Get-Date)
        # to break out from looping and making further API calls after first call, when not all resources in first fetch are modified after threshold date
        $breakLoop = $false 

        while ([System.Uri]::TryCreate($resourceDfnUrl, [System.UriKind]::Absolute, [ref] $validatedUri)) {
            if ([string]::IsNullOrWhiteSpace($orginalUri)) {
                $orginalUri = $validatedUri.AbsoluteUri;   
            }
            $progressCount = 0;
            $applicableDefnsObj=@();
            $skipCount += 10000;
            $responseAndUpdatedUri = [WebRequestHelper]::InvokeWebRequestForResourcesInBatch($validatedUri, $orginalUri, $skipCount,$resourceType);
            #API response with resources
            $resourceDefnsObj = @($responseAndUpdatedUri[0]);
            #count of all resources fetched
            $totalCount = $resourceDefnsObj.Count
            #updated URI: null when there is no continuation token
            $resourceDfnUrl = $responseAndUpdatedUri[1];

            if($isFolderPathGiven -and $isFolderSizegt100)
            {
                $applicableDefnsObj = $resourceDefnsObj | Where-Object {$_.path -eq "\$($path)" -or $_.path -replace '\s','' -match [System.Text.RegularExpressions.Regex]::Escape("$($path -replace '\s','')")}
            }
            #in case its not a folder based scan or folder cnt <100
            else 
            {
                $applicableDefnsObj=$resourceDefnsObj;
            }
            if ( (($applicableDefnsObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($applicableDefnsObj[0], "name")) -or ([Helpers]::CheckMember($applicableDefnsObj, "count") -and $applicableDefnsObj[0].count -gt 0)) 
            {
                if($resourceType -eq "build"){
                    $tempLink=($applicableDefnsObj[0].url -split('Definitions/'))[0].replace('_apis/build/', '_build?definitionId=');
                }
                else {
                    $tempLink = "https://dev.azure.com/{0}/{1}/_release?_a=releases&view=mine&definitionId=" -f $this.OrganizationContext.OrganizationName, $projectName;
                                   
                }
                if($this.UseIncrementalScan -eq $true)
                {
                    $updateTimestamp = $true
                    if(-not [string]::IsNullOrWhiteSpace($resourceDfnUrl))
                    {
                        $updateTimestamp = $false
                    }
                    $incrementalScanHelperObj = [IncrementalScanHelper]::new($this.OrganizationContext.OrganizationName, $projectName, $this.IncrementalDate, $updateTimestamp, $timestamp)
                    if($resourceType -eq "build")
                    {
                        $applicableDefnsObj = @($incrementalScanHelperObj.GetModifiedBuilds($applicableDefnsObj))
                        if($applicableDefnsObj.Count -lt $totalCount -and $updateTimestamp -eq $false)
                        {
                            # a continuation token was previously found but no need for making more API calls as even some resources in the first batch are unmodified since last threshold timestamp
                            # update Incremental Scan Helper Object data member UpdateTime to $true, then call function to Update Timestamp
                            $incrementalScanHelperObj.UpdateTime = $true
                            $incrementalScanHelperObj.UpdateTimeStamp("Build")
                            $breakLoop = $true
                        }
                    }
                    else 
                    {
                        $applicableDefnsObj = @($incrementalScanHelperObj.GetModifiedReleases($applicableDefnsObj))    
                    }
                }
                if($applicableDefnsObj.Count -lt $nObj.Value)
                {
                    $nObj.Value = $applicableDefnsObj.Count;
                }
                foreach ($resourceDef in $applicableDefnsObj) {
                    #$link = $resourceDef.url.split('?')[0].replace('_apis/build/Definitions/', '_build?definitionId=');
                    $link=$tempLink+$resourceDef.id
                    $resourceId = "organization/$organizationId/project/$projectId/$($resourceType)/$($resourceDef.id)";
                    if($resourceType -eq "build"){
                        $this.AddSVTResource($resourceDef.name, $resourceDef.project.name, "ADO.Build", $resourceId, $resourceDef, $link);

                    }
                    else {
                        $this.AddSVTResource($resourceDef.name, $projectName, "ADO.Release", $resourceId, $null, $link);

                    }
                                        
                    Write-Progress -Activity "Fetching $($resourceType)s in batches. This may take time. Fetched $($progressCount) of $(($applicableDefnsObj | Measure-Object).Count) $($resourceType)s of batch $($batchCount) " -Status "Progress: " -PercentComplete ($progressCount / ($applicableDefnsObj | Measure-Object).Count * 100)
                    $progressCount = $progressCount + 1;
                    if (--$nObj.Value -eq 0) { break; }
                }
                $batchCount = $batchCount + 1;                             

            }
            else {
                break;
            }
            if ($nObj.Value -eq 0) { break; }
            if($breakLoop -eq $true) { break; }
        }
        Write-Progress -Activity "All $($resourceType)s fetched" -Status "Ready" -Completed
        $resourceDefnsObj = $null;
        $applicableDefnsObj=$null;
        Remove-Variable resourceDefnsObj;
        Remove-Variable applicableDefnsObj;
    }

    [void] createServiceConnectionSVTResource($projectName,$organizationId,$projectId,$projectData){
        $serviceEndpointURL = ("https://dev.azure.com/{0}/{1}/_apis/serviceendpoint/endpoints?includeDetails=True&api-version=6.0-preview.4") -f $($this.organizationName), $($projectName);
                        $serviceEndpointObj = [WebRequestHelper]::InvokeGetWebRequest($serviceEndpointURL)
                        $TotalSvc += ($serviceEndpointObj | Measure-Object).Count
                        # service connection count here
                        $projectData["serviceConnections"] = ($serviceEndpointObj | Measure-Object).Count;

                        if (([Helpers]::CheckMember($serviceEndpointObj, "count") -and $serviceEndpointObj[0].count -gt 0) -or (($serviceEndpointObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($serviceEndpointObj[0], "name"))) {
                            # Currently get only Azure Connections as all controls are applicable for same

                            $Connections = $null;
                            if ($this.ServiceConnections -eq "*") {
                                $Connections = $serviceEndpointObj #| Where-Object { ($_.type -eq "azurerm" -or $_.type -eq "azure" -or $_.type -eq "git" -or $_.type -eq "github" -or $_.type -eq "externaltfs" -or $_.type -eq "externalnpmregistry" -or $_.type -eq "generic" -or $_.type -eq "externalnugetfeed" -or $_.type -eq "PRSS" -or $_.type -eq "ESRPScan") }
                            }
                            else {
                                #If service id based scan then filter with serviceconnection ids
                                if ($this.isServiceIdBasedScan -eq $true) {
                                    $Connections = $serviceEndpointObj | Where-Object {  ($this.ServiceConnectionIds -eq $_.Id) }  # ($_.type -eq "azurerm" -or $_.type -eq "azure" -or $_.type -eq "git" -or $_.type -eq "github" -or $_.type -eq "externaltfs" -or $_.type -eq "externalnpmregistry" -or $_.type -eq "generic" -or $_.type -eq "externalnugetfeed" -or $_.type -eq "PRSS" -or $_.type -eq "ESRPScan") -and
                                }
                                else {
                                    $Connections = $serviceEndpointObj | Where-Object {  ($this.ServiceConnections -eq $_.name) }  # ($_.type -eq "azurerm" -or $_.type -eq "azure" -or $_.type -eq "git" -or $_.type -eq "github" -or $_.type -eq "externaltfs" -or $_.type -eq "externalnpmregistry" -or $_.type -eq "generic" -or $_.type -eq "externalnugetfeed" -or $_.type -eq "PRSS" -or $_.type -eq "ESRPScan" -or $_.type -eq "servicefabric") -and
                                }
                            }
                            $ScannableSvc += ($connections | Measure-Object).Count

                            #Initialising null to SecurityNamespaceId variable for new scan, it is static variable, setting once only in svc class and same value is applicable for all the svc con withing org
                            [ServiceConnection]::SecurityNamespaceId = $null;
                            $serviceEndpointObj = $null;
                            Remove-Variable  serviceEndpointObj;
                            $nObj = $this.MaxObjectsToScan
                            foreach ($connectionObject in $Connections) {
                                $resourceId = "organization/$organizationId/project/$projectId/serviceconnection/$($connectionObject.Id)";
                                $link = "https://dev.azure.com/$($this.organizationName)/$projectId/_settings/adminservices?resourceId=$($connectionObject.Id)";
                                $this.AddSVTResource($connectionObject.name, $projectName, "ADO.ServiceConnection", $resourceId, $connectionObject, $link);

                                if (--$nObj -eq 0) { break; }
                            }
                        }
    }

    [void] createBuildSVTResource($projectName,$organizationId,$projectId){
        if(-not [string]::IsNullOrEmpty($this.BuildsFolderPath)){
            # Validate folder path is valid
            $path = $this.BuildsFolderPath;
            $this.BuildsFolderPath = $this.BuildsFolderPath.Replace(' ','%20').Replace('\','%5C')
            $buildFoldersURL = "https://dev.azure.com/{0}/{1}/_apis/build/folders/{2}?api-version=6.1-preview.2"  -f $($this.OrganizationContext.OrganizationName), $projectName, $this.BuildsFolderPath
            $buildFoldersObj = [WebRequestHelper]::InvokeGetWebRequest($buildFoldersURL)
            if($null -eq $buildFoldersObj -or (![Helpers]::CheckMember($buildFoldersObj[0],"Path"))){
                $this.PublishCustomMessage("Folder path not found. Please validate the -BuildsFolderPath provided in the command. `n", [MessageType]::Warning);
            }
            else {
                #Iterate on each folder to get applicale build definition if folders count is le 100
                if ([string]::IsNullOrEmpty($topNQueryString)) {
                    $topNQueryString = '&$top=10000'
                }
                $nObj=$this.MaxObjectsToScan;
                if($buildFoldersObj.Count -le 100)
                {
                    $folderCount=1
                    
                    foreach($path in $buildFoldersObj.Path)
                    {
                        
                        $formattedPath = $path.Replace(' ','%20').Replace('\','%5C')
                        $buildDefByFolderURL = ('https://dev.azure.com/{0}/{1}/_apis/build/definitions?path={2}&queryOrder=lastModifiedDescending'+$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $projectName, $formattedPath
                        Write-Progress -Activity "Searching in folder $($folderCount) of $($buildFoldersObj.Count) : $($path) " -Status "Progress: " -PercentComplete ($folderCount/ $buildFoldersObj.Count * 100)
                        $this.addResourceToSVT($buildDefByFolderURL,"build",$projectName,$organizationId,$projectId,$true,$false,$null,[ref]$nObj)
                        if($nObj -eq 0) {break;}
                        $folderCount++;
                    }
                    Write-Progress -Activity "All builds fetched" -Status "Ready" -Completed
                }
                else {                                 
                    $buildDefURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?queryOrder=lastModifiedDescending&api-version=6.0" + $topNQueryString) -f $($this.OrganizationContext.OrganizationName), $projectName;
                    $this.addResourceToSVT($buildDefURL,"build",$projectName, $organizationId, $projectId, $true, $true, $path,[ref]$nObj)                                  
                }

            }
        }
        elseif ($this.BuildNames -eq "*") {
            if ([string]::IsNullOrEmpty($topNQueryString)) {
                $topNQueryString = '&$top=10000'
                $buildDefnURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?queryOrder=lastModifiedDescending&api-version=6.0" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $projectName;
            }
            else {
                $buildDefnURL = ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?api-version=6.0" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $projectName;
            }
            $nObj=$this.MaxObjectsToScan
            $this.addResourceToSVT($buildDefnURL,"build",$projectName,$organizationId,$projectId,$false,$false,$null,[ref]$nObj);
            }
        
        else {

            $buildDefnURL = "";
            #If service id based scan then will break the loop after one run because, sending all build ids to api as comma separated in one go.
            for ($i = 0; $i -lt $this.BuildNames.Count; $i++) {
                #If service id based scan then send all build ids to api as comma separated in one go.
                if ($this.isServiceIdBasedScan -eq $true) {
                    $buildDefnURL = "https://{0}.visualstudio.com/{1}/_apis/build/definitions?definitionIds={2}&api-version=6.0" -f $($this.OrganizationContext.OrganizationName), $projectName, ($this.BuildIds -join ",");
                }
                else { #If normal scan (not service id based) then send each build name in api one by one.
                    $buildDefnURL = "https://{0}.visualstudio.com/{1}/_apis/build/definitions?name={2}&api-version=6.0" -f $($this.OrganizationContext.OrganizationName), $projectName, $this.BuildNames[$i];
                }
                $buildDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($buildDefnURL)
                if (([Helpers]::CheckMember($buildDefnsObj, "count") -and $buildDefnsObj[0].count -gt 0) -or (($buildDefnsObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($buildDefnsObj[0], "name"))) {
                    foreach ($bldDef in $buildDefnsObj) {
                        $link = $bldDef.url.split('?')[0].replace('_apis/build/Definitions/', '_build?definitionId=');
                        $buildResourceId = "organization/$organizationId/project/$projectId/build/$($bldDef.id)";
                        $this.AddSVTResource($bldDef.name, $bldDef.project.name, "ADO.Build", $buildResourceId, $bldDef, $link);

                    }
                    $buildDefnsObj = $null;
                    Remove-Variable buildDefnsObj;
                }
                #If service id based scan then no need to run loop as all the build ids has been sent to api as comma separated list in one go. so break the loop.
                if ($this.isServiceIdBasedScan -eq $true) {
                    break;
                }
            }
        }
    }

    [string] FindResourceTypeFromPartialScan($nonScannedResourceId){
        $type="";
        switch -wildcard ($nonScannedResourceId) {
            "*/build/*" {$type="ADO.Build";Break;}
            "*/release/*" {$type="ADO.Release"; Break}
            "*/agentpool/*" {$type="ADO.AgentPool"; Break}
            "*/serviceconnection/*" {$type="ADO.ServiceConnection"; Break}
            "*/user" {$type="ADO.User"; Break}
            "*/project/*" {$type="ADO.Project"}
            
            Default {$type="ADO.Organization"}
        }
        return $type;
    }

    [string] CreateResourceLinkFromPartialScan($nonScannedResourceId,$resourceType,$orgName,$projName,$projId){
        $resourceLink="https://dev.azure.com/{0}/" -f $($orgName);
        switch ($resourceType) {
           
            "ADO.Release" {
                $definitionId=($nonScannedResourceId -split('/release/'))[1];
                $resourceLink+=$projName+"/_release?_a=releases&view=mine&definitionId="+$definitionId;
                Break
            }
            "ADO.AgentPool" {
                $definitionId=($nonScannedResourceId -split('/agentpool/'))[1];
                $resourceLink+=$projId+"/_settings/agentqueues?queueId="+$definitionId+"&view=security";
                Break
            }
            "ADO.ServiceConnection" {
                $definitionId=($nonScannedResourceId -split('/serviceconnection/'))[1];
                $resourceLink+=$projId+"/_settings/adminservices?resourceId="+$definitionId;
                Break
            
            }
            "ADO.Project" {
                $resourceLink+=$projId+"/_settings/";
                Break
            }
            "ADO.Organization" {
                $resourceLink+="_settings";
                Break
            }
            "ADO.User" {
                $resourceLink+="_settings/users";
            }
            Default {}
        }
        return $resourceLink
    }

    
    [void] FetchControlFixBackupFile($orgName, $projName, $internalId)
    {
        [ControlHelper]::ControlFixBackup = @()
        $BackupControlStateRootFolder = (Join-Path $([Constants]::AzSKAppFolderPath) "TempState" | Join-Path -ChildPath "BackupControlState");
        if($internalId -match "Organization")
        {
            $BackupControlStateControlJson = (Join-Path $BackupControlStateRootFolder $orgName)
        }
        else
        {
            $BackupControlStateControlJson = (Join-Path (Join-Path $BackupControlStateRootFolder $orgName) $projName)
        }
        $fileName = $internalId + ".json"
        if(Test-Path (Join-Path $BackupControlStateControlJson $fileName))
        {
            [ControlHelper]::ControlFixBackup += Get-Content (Join-Path $BackupControlStateControlJson $fileName) -Raw | ConvertFrom-Json
        }
        else {
            $this.PublishCustomMessage("`nBackup of control data object not found. Please run GADS with -PrepareforControlFix param to generate the backup.",[MessageType]::Warning);
        }
    }

    [void] addBuildsToSvtInBatchScan($ProjectName,$ProjectId,$Path){
        [BatchScanManager] $batchScanMngr = [BatchScanManager]:: GetInstance();
        $batchStatus= $batchScanMngr.GetBatchStatus();

        #all builds have been scanned
        if([string]::IsNullOrEmpty($batchStatus.BuildCurrentContinuationToken) -and $batchStatus.Skip -gt 0){
           
            return;
        }
        $topNQueryString = '&$top={0}' -f $batchScanMngr.GetBatchSize();
        
        if($null -ne $batchStatus.BuildCurrentContinuationToken){
            $buildDefURL= ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?queryOrder=lastModifiedDescending&api-version=6.0&%24skip={2}&continuationToken={3}" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $ProjectName, $batchStatus.Skip, $batchStatus.BuildCurrentContinuationToken;
        }
        else {
            $buildDefURL= ("https://dev.azure.com/{0}/{1}/_apis/build/definitions?queryOrder=lastModifiedDescending&api-version=6.0&%24skip={2}" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $ProjectName, $batchStatus.Skip;
        }
        $updatedUriAndContToken=[WebRequestHelper]:: InvokeWebRequestForContinuationToken($buildDefURL,$buildDefURL,$null,'build');
        $continuationToken=$updatedUriAndContToken[0];
        $buildDefnsObj=$updatedUriAndContToken[2];

        if($null -ne $Path){            
                  
            $buildDefnsObj = $buildDefnsObj | Where-Object {$_.path -eq "\$($Path)" -or $_.path -replace '\s','' -match [System.Text.RegularExpressions.Regex]::Escape("$($Path -replace '\s','')")}
       
        }
        $progressCount=1
        if (([Helpers]::CheckMember($buildDefnsObj, "count") -and $buildDefnsObj[0].count -gt 0) -or (($buildDefnsObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($buildDefnsObj[0], "name"))) {
            foreach ($bldDef in $buildDefnsObj) {
                $link = $bldDef.url.split('?')[0].replace('_apis/build/Definitions/', '_build?definitionId=');
                $buildResourceId = "organization/$($this.OrganizationContext.OrganizationId)/project/$ProjectId/build/$($bldDef.id)";
                $this.AddSVTResource($bldDef.name, $bldDef.project.name, "ADO.Build", $buildResourceId, $bldDef, $link);
                Write-Progress -Activity "Fetched $($progressCount) out of $(($buildDefnsObj | Measure-Object).Count) builds " -Status "Progress: " -PercentComplete ($progressCount / ($buildDefnsObj | Measure-Object).Count * 100)
                $progressCount+=1
            }
            $buildDefnsObj = $null;
            Remove-Variable buildDefnsObj;
        }
        Write-Progress -Activity "All builds fetched" -Status "Ready" -Completed
        $batchStatus.BuildNextContinuationToken=$continuationToken;
        $batchStatus.TokenLastModifiedTime=[DateTime]::UtcNow;
        $batchScanMngr.BatchScanTrackerObj = $batchStatus;
        $batchScanMngr.WriteToBatchTrackerFile();
        
    }

    [void] addReleasesToSvtInBatchScan($ProjectName,$ProjectId,$Path){
        [BatchScanManager] $batchScanMngr = [BatchScanManager]:: GetInstance();
        $batchStatus= $batchScanMngr.GetBatchStatus();

        #all releases have been scanned
        if([string]::IsNullOrEmpty($batchStatus.ReleaseCurrentContinuationToken) -and $batchStatus.Skip -gt 0){
            return;
        }
        $topNQueryString = '&$top={0}' -f $batchScanMngr.GetBatchSize();
        if($null -ne $batchStatus.ReleaseCurrentContinuationToken){
            $releaseDefURL= ("https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?api-version=6.0&continuationToken={2}" +$topNQueryString) -f $($this.OrganizationContext.OrganizationName), $ProjectName, $batchStatus.ReleaseCurrentContinuationToken;
        }
        else {
            $releaseDefURL= ("https://vsrm.dev.azure.com/{0}/{1}/_apis/release/definitions?api-version=6.0" +$topNQueryString ) -f $($this.OrganizationContext.OrganizationName), $ProjectName;
        }
        $updatedUriAndContToken=[WebRequestHelper]:: InvokeWebRequestForContinuationToken($releaseDefURL,$releaseDefURL,$null,'release');
        $continuationToken=$updatedUriAndContToken[0];
        $releaseDefnsObj=$updatedUriAndContToken[2];

        if($null -ne $Path){            
                  
            $releaseDefnsObj = $releaseDefnsObj | Where-Object {$_.path -eq "\$($Path)" -or $_.path -replace '\s','' -match [System.Text.RegularExpressions.Regex]::Escape("$($Path -replace '\s','')")}
       
        }
        $progressCount=1
        if (([Helpers]::CheckMember($releaseDefnsObj, "count") -and $releaseDefnsObj[0].count -gt 0) -or (($releaseDefnsObj | Measure-Object).Count -gt 0 -and [Helpers]::CheckMember($releaseDefnsObj[0], "name"))) {
            $tempLink = "https://dev.azure.com/{0}/{1}/_release?_a=releases&view=mine&definitionId=" -f $this.OrganizationContext.OrganizationName, $projectName;
            foreach ($releaseDef in $releaseDefnsObj) {
                $link = $tempLink+$releaseDef.id
                $releaseResourceId = "organization/$($this.OrganizationContext.OrganizationId)/project/$ProjectId/release/$($releaseDef.id)";
                $this.AddSVTResource($releaseDef.name, $ProjectName, "ADO.Release", $releaseResourceId, $null, $link);
                Write-Progress -Activity "Fetched $($progressCount) out of $(($releaseDefnsObj | Measure-Object).Count) releases " -Status "Progress: " -PercentComplete ($progressCount / ($releaseDefnsObj | Measure-Object).Count * 100)
                $progressCount+=1
            }
            $releaseDefnsObj = $null;
            Remove-Variable releaseDefnsObj;
        }
        Write-Progress -Activity "All releases fetched" -Status "Ready" -Completed
        $batchStatus.ReleaseNextContinuationToken=$continuationToken;
        $batchStatus.TokenLastModifiedTime=[DateTime]::UtcNow;
        $batchScanMngr.BatchScanTrackerObj = $batchStatus;
        $batchScanMngr.WriteToBatchTrackerFile();
        
    }


}
# SIG # Begin signature block
# MIIjiAYJKoZIhvcNAQcCoIIjeTCCI3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAM1Ci3HuBFlrb5
# PH2/0k3R3GO/e4+s0YuxOcm4dknkDaCCDYEwggX/MIID56ADAgECAhMzAAAB32vw
# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn
# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw
# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS
# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG
# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh
# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH
# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS
# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp
# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok
# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4
# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao
# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD
# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt
# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G
# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+
# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82
# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVXTCCFVkCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
# BglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgcbNtQ1HP
# gyV3bAoApexfpelgP5UauVOd3EccA4e9szYwRAYKKwYBBAGCNwIBDDE2MDSgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRyAGmh0dHBzOi8vd3d3Lm1pY3Jvc29mdC5jb20g
# MA0GCSqGSIb3DQEBAQUABIIBABtoCIA46jsCoGKzuApH2OCbZFqFMUxXZXPYLdRd
# wUvejJ2jU2ZP8T/uD+2qtogl/zmc4DDxIvRtksMJswZiWjXn68ulmVl1OBjKmfGB
# 6Y3fTKiAojqpEftNBkFy6vnjMXhCzHtizBplNqC7rrCAJ4LBujgllM0OGiFPRM4X
# 3eJkxnEzYo1yrYgiQY66o6BAE8sw0Lo3ypcp98Ecbupv5Z3lgersh8YP/C8+rCAi
# TWf2b0tlqRs4rPq2nfAy8vL3qKF5gpliPydSQYdOZY8w0/W1HV0oNezYjlAgIEK6
# 659TyW6J0Iv9Lds+cmRPvVY+js+MW4NHMYR3cw8ZzDCb+eChghLlMIIS4QYKKwYB
# BAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFl
# AwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkK
# AwEwMTANBglghkgBZQMEAgEFAAQgL/WQGgAIvAyZ8b3lpNO+YB9Qynn9ubNnqVCv
# P3sP6GUCBmD5hOutHhgTMjAyMTA4MTYwNTUzNDIuMDEzWjAEgAIB9KCB0KSBzTCB
# yjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMc
# TWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRT
# UyBFU046MjI2NC1FMzNFLTc4MEMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAUqk9zHE/yKiSQAAAAAB
# SjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe
# Fw0yMDExMTIxODI1NThaFw0yMjAyMTExODI1NThaMIHKMQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj
# YSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoyMjY0LUUzM0Ut
# NzgwQzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN7KKGZkolgsvVEaNKMTVZZgEl8o
# hsLgHR4gUFWLZvWzLbegDoRItpfFd+9maW2hPFlgT+wv7lxf6OB4HFYZgHfIpcZh
# GU6/ebsymXYAmAKzKph71pxJU5F228YTSTLcoSAIUNBZVdTEIZILEPT5gI77Ysu7
# YKMufSmiZPzqYlkEX2/dHhOcoo90zgIJRTG1u2kF7w6a7D50yHKE46eGEwqwjExE
# RCCNtFBDQrTfYID/Icj0zKikYjiJRaaPNjnvBaRJ/eFkGz8gD2XyYXjlsNjDGPaG
# PQTt/Rm3nrxcyXGyCIIhWdBMXMTLl7BMDKKeLBQ0d6pFfS1LRJo+paKKBiUCAwEA
# AaOCARswggEXMB0GA1UdDgQWBBRxjGEYMfrAhjWKk/99frgmKqk/4TAfBgNVHSME
# GDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRw
# Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQ
# Q0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5o
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8y
# MDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMI
# MA0GCSqGSIb3DQEBCwUAA4IBAQBRC67dRjgFFS9kv72Vfe8gQ+Hg3FpX2TYyOq4n
# rtPq9D36Udydr2ibZy5n7LphXvW20bDTugUHiwuyfWnmyc2oEevo+SrNCzxXcj59
# Wv9lQpBgtL6OM56x+v1zbNzp/moMwk3UvysE5af5rktfFtPx6apqcjU1IDt09hX8
# 0ZAzqPflPPyC5Cj3J8DQilQz2/TzSZvcbgCM9vuwLu9p9bZhJemNP++3LrHkdycf
# HZf3jv7QBAigEvyVb2mrnlomFIKCyJW1cOrBjIqyntQt5PK8zKxX/yZlyiRbr8c0
# DQw8tYpXeyorgoVet9sAF+t3g/cYzVogW4qwhuyZmEmTlTSKMIIGcTCCBFmgAwIB
# AgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAx
# MjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw
# JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZI
# hvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoA
# goX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiE
# VEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+B
# VLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3w
# V3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXo
# eByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYw
# ggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNo
# WoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW
# BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH
# AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
# L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGV
# MIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIw
# NB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4A
# dAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM
# 9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0
# YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgP
# F/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/62
# 5Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZq
# kHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96
# LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5v
# vfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiF
# AR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduW
# sqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV
# 42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto2
# 29Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNh
# IE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjIyNjQtRTMzRS03
# ODBDMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw
# BwYFKw4DAhoDFQC8BO6GhSDKwTN3KQTVtEHiiHprmKCBgzCBgKR+MHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA5MRPgDAiGA8y
# MDIxMDgxNjEwNDQxNloYDzIwMjEwODE3MTA0NDE2WjB3MD0GCisGAQQBhFkKBAEx
# LzAtMAoCBQDkxE+AAgEAMAoCAQACAg3NAgH/MAcCAQACAhEmMAoCBQDkxaEAAgEA
# MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI
# AgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbmFerdudGPJjHLPqqbQnFrOg0v54
# xKMUi6jC47X7JbCZY+LevusFoGfLihwS7OH2DqPme5NpaLiUqvKhEdz7XXHtsgD/
# kZGTA+xH5lN4R62sExjy5TtmTJ+aRZ2zaN+b7MmBb8tEA1uQ+3EGnwgRAoXA0zP5
# BUwIP6fxpPAL424xggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMAITMwAAAUqk9zHE/yKiSQAAAAABSjANBglghkgBZQMEAgEFAKCCAUow
# GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCAfqeZH
# Q7o/3oc0yyc1S6DkkQVDLyzWstYzgtvEQSzxHjCB+gYLKoZIhvcNAQkQAi8xgeow
# gecwgeQwgb0EIGwdktetudtX/kn7Yq/AVYiBWZBq+n4EFVQ8zUD3IlEDMIGYMIGA
# pH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFKpPcxxP8iokkA
# AAAAAUowIgQgIvnPJ//JJp+u/Ki/+NvVbmLlH2QE/4F7B/re9xfS0LUwDQYJKoZI
# hvcNAQELBQAEggEANwLrIYHSZchdeacNr2hLMHOApA7DIt2I6omq7yVmu+UC+7XD
# t30POl6WeAxz5u5GGKTcWSLOk6BAe+HqSrcVu81d7aKpPXV6sqtzYGUNY07ZufR1
# lRbzywwKmpHwXUfhdD7Te3nW1uc3TeHwQj+O2af/Btwujd18J76J1qob4frRp7Yc
# h7ugpfkzasGfDGiOMlJyhY3rVr9ebg+6i8O7AdUVU1Uiv8tQPScY4ni7s3fogC0L
# qBJRHtwHf3gFWNW+MclRuulgQGLr02ly5GRHUyCmg4ghlaqfuPcdKEAqaXuYj+qF
# ivrs6u4N95Bd6KnHzs4qIqvJy1hdC6O8Wmi2Fw==
# SIG # End signature block