Лучший отвечающий
Runbook looping

Вопрос
-
We are trying to build a runbook for creating AD users using information from our CRM and report to them
When I run this runbook if all steps finished success everything is OK, but if one step failed (3rd step for example can return that User already exist) this error message collect at Fail Message Collector and run next step Report to our CRM
When runbook finished it start automatically from the begin. If we Disable last step looping stops and everything is OK but we need to take error message in our CRM. We tried to combine Fail Collector and CRM Result to one block but runbook still looping.
The opinion expressed by me is not an official position of Microsoft
- Изменено Vector BCO 10 марта 2017 г. 13:18
10 марта 2017 г. 13:17
Ответы
-
Hi dear fellows
I found a problem. It was in the last steps named "CRM Result ...". When we started build this runbook in the activity "CRM Result" we puted some variable in the published data, but we dont use it atall and it was emptyWhen we found and remove this variable from Published data in last steps runbook start working as it should
The opinion expressed by me is not an official position of Microsoft
- Помечено в качестве ответа Vector BCO 29 июля 2017 г. 7:23
29 июля 2017 г. 7:23
Все ответы
-
Share the code of Get-Aduser..
Regards
Priyabrata
10 марта 2017 г. 16:56 -
Hi,
I guess the Runbook is triggered from another Runbook with "Invoke Runbook" Activity, which has looping enbaled an exit condition like this:
Ig you don't want the looping disable in the other Runbook the loop at"Invoke Runbook".
Regards,
Stefan
Visit go2azure.eu and my blog at www.sc-orchestrator.eu !
13 марта 2017 г. 8:33Отвечающий -
Dear Stefan, I have only one runbook, and start it manually (in test mode) from Web console.
In web console I chouse 2 parameters - Runbook server, and number of action in our CRM.
On second step "Request parameters", I found action by number from Init_D, and get parameters like FirstName, LastName, PhoneNuber etc.
On third step "Validation" I try to find users with this parameters, if user exist I invoke exception
If user exist - 4st step is "Fail message collector" - on this step I put error message to the string like "On a first validation action faild. Error: $FirstValidationError"
On a last step I try to write message from step 4 to our CRM. I found CRM action using number from Init_D, and put message to the CRM action log.
When I run my runbook manually and user exist I take alot messages in my CRM
If user does not exist everything run once
The opinion expressed by me is not an official position of Microsoft
- Изменено Vector BCO 13 марта 2017 г. 9:04
13 марта 2017 г. 8:38 -
Share the code of Get-Aduser..
Regards
Priyabrata
$GivenName = "{GivenName from "Request Parameters from CRM"}" $Surname = "{Surname from "Request Parameters from CRM"}" $SamAccountName = "{SamAccountName from "Request Parameters from CRM"}" try { $ErrorActionPreference = "Stop" "$(get-date -Format u) : Start" | out-file C:\tmp\trace.log $argsArray = @() "$(get-date -Format u) : args arr init" | out-file C:\tmp\trace.log -Append $argsArray += "$GivenName" $argsArray += "$Surname" $argsArray += "$SamAccountName" "$(get-date -Format u) : Arr: $argsArray" | out-file C:\tmp\trace.log -Append $dc = Invoke-Command -ComputerName localhost -ScriptBlock { (([system.directoryservices.activedirectory.Forest]::GetCurrentForest()).Domains.Domaincontrollers).name | foreach { if (!([string]::IsNullOrEmpty($_))){ IF (Test-Connection $_ -Count 2 -Quiet){$dc = $_} } # End If } # End Foreach if ([string]::IsNullOrEmpty($dc)){ Throw "Cannot find valid DC" } # End If else {$dc} } # End Scriptblock "$(get-date -Format u) : Before Session create. Computername: '$dc'" | out-file C:\tmp\trace.log -Append # Establish an external session (to localhost) to ensure 64bit PowerShell runtime using the latest version of PowerShell installed on the runbook server # Use this session to perform all work to ensure latest PowerShell features and behavior available $Session = New-PSSession -ComputerName $dc -Authentication Kerberos if (!([string]::IsNullOrEmpty($Session))){ "$(get-date -Format u) : Session created succesfully" | out-file C:\tmp\trace.log -Append } # Invoke-Command used to start the script in the external session. Variables returned by script are then stored in the $ReturnArray variable $ReturnArray = Invoke-Command -Session $Session -Argumentlist $argsArray -ScriptBlock { # Define a parameter to accept each data bus input value. Recommend matching names of parameters and data bus input variables above Param( [ValidateNotNullOrEmpty()] [string]$GivenName, [Parameter(Mandatory = $false)] [string]$Surname, [Parameter(Mandatory = $false)] [string]$SamAccountName ) # End Param Import-Module ActiveDirectory $retry = 0 $UserEnabled = $false $UserDoesNotExist = $false do { try { if (! [string]::IsNullOrEmpty($SamAccountName)){ $AdUser = Get-ADUser $SamAccountName -Properties * -ErrorAction stop Switch ($AdUser.UserAccountControl) { 514{ # If we are here, then user now Disabled if (! ([string]::IsNullOrEmpty($AdUser.memberOf))){ foreach ($group in $AdUser.memberOf){ $group -match 'CN=(?''GRP''[^,]+),' | Out-Null Remove-ADGroupMember -Identity $Matches['GRP'] -Members $SamAccountName -Confirm:$false } # End Foreach } # End If } # End If User Disabled 512 { # If we are here, then user now Enabled Throw "User '$SamAccountName' exist and enabled." } # End ElseIf (User Enabled) default { # User Exist but something is incorrect (Maybe SUZ) Throw "User '$SamAccountName' exist and status is different as expected.$($AdUser.UserAccountControl)" } # End Else (User Exist but something is incorrect) } } # End If SamAccountName Exist Else { Throw "SamAccountName is empty" } } # End try Catch{ switch -Regex ($Error[0].Exception.Message){ "Cannot find an object with identity: '$SamAccountName'" { $UserDoesNotExist = $true } # End If "User '$SamAccountName' exist and enabled."{ $UserEnabled = $true } Default { Start-Sleep -s 2 } # End Else } # End Switch $ErrorMessage = $Error[0].Exception.Message } # End try $retry++ } Until (($UserDoesNotExist) -or (! [string]::IsNullOrEmpty($AdUser)) -or ($retry -ge 5)) if (($retry -ge 5) -or ($UserEnabled)) { $ErrorMessage = $Error[0].Exception Throw $ErrorMessage } # End If $TmpDisplayName = "$GivenName $Surname" Try { $i = 1 $AdUsers = Get-ADUser -Filter * -Properties DisplayName do { if ($($AdUsers | where {($_.DisplayName -eq $TmpDisplayName) -and !($_.SamAccountName -eq $SamAccountName)} | Measure-Object).count -ge 1){ $TmpDisplayName = "$TmpDisplayName $i" $i++ } # End If else { $DisplayName = $TmpDisplayName } # } Until (!([string]::IsNullOrEmpty($DisplayName))) } # End try Catch { #$error[0] | out-file C:\tmp\trace.log -Append $ErrorMessage = $Error[0].Exception Throw $ErrorMessage } # End Catch if ($UserDoesNotExist){ $AccountExist = 0 } # End If Elseif (!($UserDoesNotExist) -and !($UserEnabled)) { $AccountExist = 1 } # End Else $resultArray = @() $resultArray += $AccountExist $resultArray += $DisplayName return $resultArray } # End Invoke-Command $AccountExist = $ReturnArray[0] $DisplayName = $ReturnArray[1] Remove-PSSession $Session -ErrorAction "SilentlyContinue" } # End Try Catch { $ErrorMessage = $Error[0] Remove-PSSession $Session -ErrorAction "SilentlyContinue" Throw $ErrorMessage } # End Catch
The opinion expressed by me is not an official position of Microsoft
- Изменено Vector BCO 13 марта 2017 г. 11:28
13 марта 2017 г. 11:18 -
Have anyone any ideas?
The opinion expressed by me is not an official position of Microsoft
13 марта 2017 г. 14:52 -
Thanks Vector, for sharing the code. I was busy attending Summit in Singapore :)
Analyzed the code. You cannot have have 2 Invoke Sessions and enter-pssession in a .NET Script. SCORCH is a 32 Bit PS.
You need to break the code.
1> Get the details-> as Inputs
2> Get the DC. (here you need to add a break command to get the first available DC. No need to loop and wait for all.
3> Pass this valued to to the Next .Net activity.
Remember that Orchestrator will use 32 bit powershell version if you use the built in powershell script function.
Therefore you do miss some functions from powershell v3 and so on..
You might not need this, you can try to log an activity in the return Array value.
# Establish an external session (to localhost) to ensure 64bit PowerShell runtime using the latest version of PowerShell installed on the runbook server
# Use this session to perform all work to ensure latest PowerShell features and behavior available
$Session = New-PSSession -ComputerName $dc -Authentication Kerberos
if (!([string]::IsNullOrEmpty($Session))){
"$(get-date -Format u) : Session created succesfully" | out-file C:\tmp\trace.log -Append
}
4> This might not work within Invoke command due to double hop. Its a great PS script but will not work in SCORCH unless and until, you break it.
$error[0] | out-file C:\tmp\trace.log -Append
5> Invoke-Command -ComputerName localhost -ScriptBlock instead write Invoke-Command -ScriptBlock {}
SCORCH goes hayware and at times does not understand -computername localhost
If you are still stuck, let us know.
Regards
Priyabrata
14 марта 2017 г. 10:55 -
Or you might be interested in this. :) . This will solve all the purpose of your issues. (No need to invoke or PSSEssion)
By Default SCORCh 2012/2016 will start 32bit PowerShell version 2 with "Run .Net Script" Activity.
You can set this Registry-Value on the Orchestrator Runbook Service to make "Run .Net Script" Activity to execute the highest version of PowerShell on the Orchestrator Runbook Server.
HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework
Reg_DWORD: OnlyUseLatestCLR
Value: 1
Regards,
Priyabrata
14 марта 2017 г. 11:00 -
Thanks Vector, for sharing the code. I was busy attending Summit in Singapore :)
Analyzed the code. You cannot have have 2 Invoke Sessions and enter-pssession in a .NET Script. SCORCH is a 32 Bit PS.
You need to break the code.
1> Get the details-> as Inputs
2> Get the DC. (here you need to add a break command to get the first available DC. No need to loop and wait for all.
3> Pass this valued to to the Next .Net activity.
Remember that Orchestrator will use 32 bit powershell version if you use the built in powershell script function.
Therefore you do miss some functions from powershell v3 and so on..
You might not need this, you can try to log an activity in the return Array value.
# Establish an external session (to localhost) to ensure 64bit PowerShell runtime using the latest version of PowerShell installed on the runbook server
# Use this session to perform all work to ensure latest PowerShell features and behavior available
$Session = New-PSSession -ComputerName $dc -Authentication Kerberos
if (!([string]::IsNullOrEmpty($Session))){
"$(get-date -Format u) : Session created succesfully" | out-file C:\tmp\trace.log -Append
}
4> This might not work within Invoke command due to double hop. Its a great PS script but will not work in SCORCH unless and until, you break it.
$error[0] | out-file C:\tmp\trace.log -Append
5> Invoke-Command -ComputerName localhost -ScriptBlock instead write Invoke-Command -ScriptBlock {}
SCORCH goes hayware and at times does not understand -computername localhost
If you are still stuck, let us know.
Regards
Priyabrata
Dear Priyabrata,
I know that SCO run PowerShell script in 32 bit process in lowest version - it is strange, by the way.
I tried modify registry as you wrote at the top, but it broke all. I take errors on first step with message "You cannot call method on a null-value expression".
I"m rotated back my values in registry, and try split my code in validation part to the 2 Activities (its not looks like believable, but i try), and I got the same problems with looping as before.
Code of this Activitie (Validation) writen for test, and it work clearly, but i got looping of all RunBook, and got now ideas why this sh#t happen. Some of yours proposal optimizations will be accepted in the production version, but now it does not take sense. Can you give a proufe of this "You cannot have have 2 Invoke Sessions and enter-pssession in a .NET Script. SCORCH is a 32 Bit PS."I use only one PSSession in my code block in a second of time, when I open second session previous session already closen. By the way in my script block I do bot use double hope constructions
In this scheme i got looping
In this scheme I have no looping but its looks like ugly
The opinion expressed by me is not an official position of Microsoft
- Изменено Vector BCO 14 марта 2017 г. 15:46
14 марта 2017 г. 13:37 -
Hello Vector,
I have bit messed around your code to make few changes. My apologies for this.
I have changed the script of finding the First available DC.
Your code was looping around to get all the DC and was continuing to populate all the available DC as an array.
So is the reason, it was looping.
$dc = Invoke-Command -ScriptBlock { $arrayListofDC = (([system.directoryservices.activedirectory.Forest]::GetCurrentForest()).Domains.Domaincontrollers).name foreach ($AvailableDC in $arrayListofDC) { IF (Test-Connection $AvailableDC -Count 2 -Quiet) { Return $AvailableDC } Break; } # End If if ([string]::IsNullOrEmpty($AvailableDC)){ Throw "Cannot find valid DC" } # End If else {$AvailableDC} } # End Scriptblock
Modified the above code.
Instead of remoting into the server using a PS Session , pls use the AD PS Options.
#Remove-ADGroupMember -Identity $Matches['GRP'] -Members $SamAccountName -Confirm:$false #My Code Remove-ADGroupMember -Identity $Matches['GRP'] -Members $SamAccountName -Server $dc -Confirm:$false
Suit yourself to Run the code. Should work fine. :)
$GivenName = "{GivenName from "Request Parameters from CRM"}" $Surname = "{Surname from "Request Parameters from CRM"}" $SamAccountName = "{SamAccountName from "Request Parameters from CRM"}" try { $ErrorActionPreference = "Stop" "$(get-date -Format u) : Start" | out-file C:\temp\trace.log $argsArray = @() "$(get-date -Format u) : args arr init" | out-file C:\temp\trace.log -Append $argsArray += "$GivenName" $argsArray += "$Surname" $argsArray += "$SamAccountName" "$(get-date -Format u) : Arr: $argsArray" | out-file C:\temp\trace.log -Append # This will Retun the First Available DC $dc = Invoke-Command -ScriptBlock { $arrayListofDC = (([system.directoryservices.activedirectory.Forest]::GetCurrentForest()).Domains.Domaincontrollers).name foreach ($AvailableDC in $arrayListofDC) { IF (Test-Connection $AvailableDC -Count 2 -Quiet) { Return $AvailableDC } Break; } # End If if ([string]::IsNullOrEmpty($AvailableDC)){ Throw "Cannot find valid DC" } # End If else {$AvailableDC} } # End Scriptblock "$(get-date -Format u) : Before Session create. Computername: '$dc'" | out-file C:\temp\trace.log -Append # Invoke-Command used to start the script in the external session. Variables returned by script are then stored in the $ReturnArray variable $ReturnArray = Invoke-Command -Argumentlist $argsArray -ScriptBlock { # Define a parameter to accept each data bus input value. Recommend matching names of parameters and data bus input variables above Param( [ValidateNotNullOrEmpty()] [string]$GivenName, [Parameter(Mandatory = $false)] [string]$Surname, [Parameter(Mandatory = $false)] [string]$SamAccountName ) # End Param Import-Module ActiveDirectory $retry = 0 $UserEnabled = $false $UserDoesNotExist = $false do { try { if (! [string]::IsNullOrEmpty($SamAccountName)){ $AdUser = Get-ADUser $SamAccountName -Properties * -ErrorAction stop -Server $dc Switch ($AdUser.UserAccountControl) { 514{ # If we are here, then user now Disabled if (! ([string]::IsNullOrEmpty($AdUser.memberOf))){ foreach ($group in $AdUser.memberOf){ $group -match 'CN=(?''GRP''[^,]+),' | Out-Null #Remove-ADGroupMember -Identity $Matches['GRP'] -Members $SamAccountName -Confirm:$false #My Code Remove-ADGroupMember -Identity $Matches['GRP'] -Members $SamAccountName -Server $dc -Confirm:$false } # End Foreach } # End If } # End If User Disabled 512 { # If we are here, then user now Enabled Throw "User '$SamAccountName' exist and enabled." } # End ElseIf (User Enabled) default { # User Exist but something is incorrect (Maybe SUZ) Throw "User '$SamAccountName' exist and status is different as expected.$($AdUser.UserAccountControl)" } # End Else (User Exist but something is incorrect) } } # End If SamAccountName Exist Else { Throw "SamAccountName is empty" } } # End try Catch{ switch -Regex ($Error[0].Exception.Message){ "Cannot find an object with identity: '$SamAccountName'" { $UserDoesNotExist = $true } # End If "User '$SamAccountName' exist and enabled."{ $UserEnabled = $true } Default { Start-Sleep -s 2 } # End Else } # End Switch $ErrorMessage = $Error[0].Exception.Message } # End try $retry++ } Until (($UserDoesNotExist) -or (! [string]::IsNullOrEmpty($AdUser)) -or ($retry -ge 5)) if (($retry -ge 5) -or ($UserEnabled)) { $ErrorMessage = $Error[0].Exception Throw $ErrorMessage } # End If $TmpDisplayName = "$GivenName $Surname" Try { $i = 1 $AdUsers = Get-ADUser -Filter * -Properties DisplayName do { if ($($AdUsers | where {($_.DisplayName -eq $TmpDisplayName) -and !($_.SamAccountName -eq $SamAccountName)} | Measure-Object).count -ge 1){ $TmpDisplayName = "$TmpDisplayName $i" $i++ } # End If else { $DisplayName = $TmpDisplayName } # } Until (!([string]::IsNullOrEmpty($DisplayName))) } # End try Catch { #$error[0] | out-file C:\temp\trace.log -Append $ErrorMessage = $Error[0].Exception Throw $ErrorMessage } # End Catch if ($UserDoesNotExist){ $AccountExist = 0 } # End If Elseif (!($UserDoesNotExist) -and !($UserEnabled)) { $AccountExist = 1 } # End Else $resultArray = @() $resultArray += $AccountExist $resultArray += $DisplayName return $resultArray } # End Invoke-Command $AccountExist = $ReturnArray[0] $DisplayName = $ReturnArray[1] #Remove-PSSession $Session -ErrorAction "SilentlyContinue" } # End Try Catch { $ErrorMessage = $Error[0] #Remove-PSSession $Session -ErrorAction "SilentlyContinue" Throw $ErrorMessage } # End Catch
Regards,
Priyabrata
15 марта 2017 г. 3:18 -
Dear Priyabrata,
1) Find DC block is non optimized, but it return only one result or Error. Please prove something if you say that is that code is incorrect.
Here is my prove:
2) Remove-ADGroupMember with out invoke-command will fail because in PoSh v1 32bit dont now nothing about that command
Remove-ADGroupMember -Identity $Matches['GRP'] -Members $SamAccountName -Server $dc -Confirm:$false
Remove-ADGroupMember with invoke-command will fail because it will be doublehope operation, and I do not want add credentials in this script
The opinion expressed by me is not an official position of Microsoft
16 марта 2017 г. 16:01 -
Hi,
I guess "Throw $ErrorMessage" will kill the process of the Runbook Instances. Bu I can't reproduce this here (System Center 2016 running on Windows 2016).
Perhaps you can look at "C:\ProgramData\Microsoft System Center 2012\Orchestrator\PolicyModule.exe\Logs" to check.
I would not use "Throw $ErrorMessage" but I would define ErrorMessage in the Publishe Data tab and go to the error branch if ErrorMessage is not empty
Regards,
Stefan
Visit go2azure.eu and my blog at www.sc-orchestrator.eu !
17 марта 2017 г. 22:23Отвечающий -
Dear Stefan, sorry for a long respond!
We try to modify our scripts (remove throw $ErrorMessage), and build logical matches based on $ErrorMessage, as you show in a prewious post. But we still take a loop after runbook finished :-(
I think something can be wrong in a CRM activity. If we disable them - everything is ok
The opinion expressed by me is not an official position of Microsoft
- Предложено в качестве ответа Stefan HorzMVP, Editor 10 апреля 2017 г. 11:32
- Отменено предложение в качестве ответа Vector BCO 28 июля 2017 г. 13:52
22 марта 2017 г. 8:33 -
Hello Vector,
The return object is always a string .
If you see the code I have shared with you is exactly the same. If you do $arraListofDC.getType() , its an array.
$arrayListofDC = (([system.directoryservices.activedirectory.Forest]::GetCurrentForest()).Domains.Domaincontrollers).name
And from here you are sending each DC as an input parameter(as a string) to the invoke session. Hence your code is looping.
It will loop for 30 time if you have 30 DC in the array list. Hence you have a loop.
# To by pass the double hop, you can use the following link.
You will need to add the Service account to the System Group of Runbook Server.
Regards
Priyabrata
30 марта 2017 г. 14:38 -
Hello Vector,
The return object is always a string .
If you see the code I have shared with you is exactly the same. If you do $arraListofDC.getType() , its an array.
$arrayListofDC = (([system.directoryservices.activedirectory.Forest]::GetCurrentForest()).Domains.Domaincontrollers).name
And from here you are sending each DC as an input parameter(as a string) to the invoke session. Hence your code is looping.
It will loop for 30 time if you have 30 DC in the array list. Hence you have a loop.
# To by pass the double hop, you can use the following link.
You will need to add the Service account to the System Group of Runbook Server.
Regards
Priyabrata
I hope you're joking, about "the return object is always a string"
I use foreach for array and got only last DC from my list. I got only 2 DC, not 30!
In my case looped not one activity, looped all of my runbook.
Also, if you use -sessions, it mean that invoke-command will work simultaneously.
Otherwise if you use -ComputerName parameter with Invoke-Command it mean that ScriptBlock will work one by one
The opinion expressed by me is not an official position of Microsoft
- Изменено Vector BCO 30 марта 2017 г. 16:38
30 марта 2017 г. 16:37 -
If we Disable last step looping stops and everything is OK but we need to take error message in our CRM.
I'm interested in that last step, which from the picture looks to be called CRM Result: Fail. Orchestrator is like a disobedient child at times when it comes to running activities; saying success but really failing. I have first hand experience where PowerShell completely bombs out because of uncaptured stdout stream, but Orchestrator reports a success for the activity.
I wonder (and would not be surprised) if that last step is really killing off the runbook execution and Orchestrator is capturing the failure and restarting the runbook. Hence the looping.
Has this bit of code been shared yet?
30 марта 2017 г. 16:51 -
Hi dear fellows
I found a problem. It was in the last steps named "CRM Result ...". When we started build this runbook in the activity "CRM Result" we puted some variable in the published data, but we dont use it atall and it was emptyWhen we found and remove this variable from Published data in last steps runbook start working as it should
The opinion expressed by me is not an official position of Microsoft
- Помечено в качестве ответа Vector BCO 29 июля 2017 г. 7:23
29 июля 2017 г. 7:23