Запускать фоновые задачи PowerShell в текущем контексте скрипта - PullRequest
0 голосов
/ 06 декабря 2018

Я написал сценарий с графическим интерфейсом для выполнения различных задач управления в SharePoint Online (SPO).

Один конкретный вызов API для получения групп SPO занимает много времени (примерно 150 секунд).Я проверил этот вызов с помощью командлетов SPO, а также вручную получил группы с помощью DLL-библиотек SharePoint, которые занимают примерно одинаковое время.Проблема в том, что запрос к SharePoint означает, что вам нужно запросить коллекцию групп, затем перейти к каждой группе в этой коллекции, перейти и получить ее.Это большой вызов, так как сайт SPO, с которым я работаю, имеет около 300 групп.

Чтобы ускорить вызов, у меня была идея асинхронно выполнять каждый вызов, чтобы все они отправлялись сразу (вместо этогоодного за другим).

Я использовал следующие ресурсы, чтобы найти решение: Адам, блог о автоматизаторах , этот ответ SO & этот блогпо ограничению выполнения задач .

Я обнаружил, что при реализации решений из первых двух источников код работал в тестовых сценариях, но не в основном решении.Я объяснил это тем, что PowerShell попросил назначить 300 различных процессов, и моя машина (хотя и мощная) быстро использовала ресурсы ЦП и ОЗУ.Поэтому я реализовал 3-й источник, чтобы ограничить число одновременно выполняемых задач.

Теперь, когда я вызываю API SPO, у меня возникает проблема, вызванная областью выполнения процесса.Чтобы использовать библиотеку PowerShell SPO (командлеты), вам необходимо вызвать функцию соединения, которая проверяет ваши учетные данные SPO и устанавливает соединение:

Connect-SPOService -Url https://$orgName.sharepoint.com/ -Credential $credentials

Вызов не будет работать, пока вы не вызовете это.

Давайте посмотрим на мою функцию:

function Get-Sharepoint-Groups-Async {
    Begin {
        $sw = [Diagnostics.Stopwatch]::StartNew()

        Write-Debug ("Starting {0} " -f $MyInvocation.MyCommand)

        #Create an hashtable variable 
        [hashtable]$Return = @{} 
        $Return.OutCode = 0
        $Return.OutData = {}
        $Return.OutDataShort = ""
    }

    Process {
        try {
            $web = $context.Web

            $groupCollection = $web.SiteGroups

            $context.Load($groupCollection)
            $context.ExecuteQuery()

            $jobs = @()

            $maxConcurrentJobs = 25

            foreach ($spGroup in $groupCollection) {
                $Check = $false
                while ($Check -eq $false) {
                    if ((Get-Job -State 'Running').Count -lt $maxConcurrentJobs) {
                        $jobs += Start-Job -ScriptBlock $sScriptBlock -ArgumentList @($groupTitle, $context)
                        $Check = $true #To stop endless looping and proceed to the next object in the list.
                    }
                }
            }

            Wait-Job -Job $jobs | Out-Null
            $results = Receive-Job -Job $jobs

            $groups = @()
            foreach ($job in $jobs) {
                $group = New-Object PSObject
                $group | Add-Member NoteProperty LoginName $job.LoginName
                $group | Add-Member NoteProperty Title $job.Title
                $group | Add-Member NoteProperty Users $job.Users
                $group | Add-Member NoteProperty Roles $job.Roles
            }

            $Return.OutData = $groups
        } catch {
            Write-Error $Error.item(0)
            $Return.OutCode = 2
            $Return.OutData = $Error.item(0)
            $Return.OutDataShort = $Error.item(0)
        }
    }

    End {
        $sw.Stop()

        Write-Debug ("Ending {0} " -f $MyInvocation.MyCommand)

        $outmsg = "Function: Get-SharePoint-Groups-Async took '$($sw.Elapsed.TotalSeconds)s' to execute."
        Write-Host $outmsg

        return $Return
    }
}

Вот код $scriptBlock, который выполняется:

$sScriptBlock = {
    Param($spGroup, $context)

    try {  
        Write-Host -ForegroundColor DarkGreen "Loading group: $($spGroup.Title)"

        $context.Load($spGroup)
        $context.ExecuteQuery()

        $groupUsers = $spGroup.Users
        $context.Load($groupUsers)
        $context.ExecuteQuery()

        #Get group users (easier to use cmdlet then query sp)
        $spGroupUsers = Get-SPOUser -Site $Site -Group $spGroup.Title

        $users = @()

        foreach ($spUser in $spGroupUsers) {
            $users += $spUser.LoginName
        }

        $group = New-Object PSObject
        $group | Add-Member NoteProperty LoginName $spGroup.LoginName
        $group | Add-Member NoteProperty Title $spGroup.Title
        $group | Add-Member NoteProperty Users $users
        $group | Add-Member NoteProperty Roles "Unknown"

        return $group
    } catch {
        Write-Error $Error.item(0)
        return $null
    }
}

Независимо от того, что я делаю внутри блока выполнения, яне может использовать текущий контекст скрипта.Если я использую командлеты SPO, мне нужно каждый раз вызывать connect, что делает вызов настолько неэффективным, что синхронно выполнять его быстрее.Если я пытаюсь запросить API SPO с использованием контекста (который установлен в другом месте скрипта и работает везде), то я получаю сообщение об ошибке, говорящее, что методы не существуют.

Я не выполняю огромное количествоPowerShell, и я на самом деле не видел, чтобы люди делали это на SharePoint где-то еще, поэтому я немного застрял.

Есть какие-нибудь идеи о том, как я могу запрашивать SPO из моих фоновых задач, не вызывая connect каждый раз?

PS: Connect не возвращает ничего, что я могу передать этим процессам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...