none
Problems Loading AD Cmdlets in a Workflow RRS feed

  • Question

  • Hello,

    I'm still working with this, but I thought I post this to see if I can speed things up a bit. 

    I'm trying to set the logonHours attribute for a particular set of users using MIMWAL's PowerShell.  In short, the PowerShell script is:

    [byte[]]$logonHours = @(0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0x00)
    get-ADUser -Identity $AccountName
    set-aduser -identity $user -replace @{logonHours = $logonHours}

    This works from a PowerShell window.  It doesn't not work running under the workflow.  Throws this error:

    WAL (2.16.0320.0): 01/24/2017 09:35:28.9077: RunPowerShellScript : RunScript: PowerShell script execution resulted in 2 error(s):\nThe term 'get-ADUser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

    The term 'set-aduser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

    I tried the import-module ActiveDirectory at the beginning of the script and get this error:

    WAL (2.16.0320.0): 01/24/2017 09:15:55.2184: RunPowerShellScript : <SetupStreamEventHandlers>b__0: The 'C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1' module cannot be imported because its manifest contains one or more members that are not valid. The valid manifest members are ('ModuleToProcess', 'NestedModules', 'GUID', 'Author', 'CompanyName', 'Copyright', 'ModuleVersion', 'Description', 'PowerShellVersion', 'PowerShellHostName', 'PowerShellHostVersion', 'CLRVersion', 'DotNetFrameworkVersion', 'ProcessorArchitecture', 'RequiredModules', 'TypesToProcess', 'FormatsToProcess', 'ScriptsToProcess', 'PrivateData', 'RequiredAssemblies', 'ModuleList', 'FileList', 'FunctionsToExport', 'VariablesToExport', 'AliasesToExport', 'CmdletsToExport'). Remove the members that are not valid ('HelpInfoUri'), then try to import the module again.

    I read something about adding a startup tag to the FIMServer config file.  Did that and the FIMService won't start with that in there.  Maybe I'm not putting the tag in the right place.  But, I stuck it as a child node to the configuration tag. 

    I don't want to get into the code extensions, as that would require my client to maintain a developer for this, which they don't want. So, I'm trying to stay "in the box".  I haven't seen anything about being able to do this with sync rules.  What I've read suggests a rules extension.  I just need to get the PowerShell script MIMWAL to run.

    Any ideas? 

    Greg

    Tuesday, January 24, 2017 4:58 PM

Answers

  • You are going to gain significant performance by using a rules extension.  Even though some customers will panic when they hear the word code, they will also freak out when things take a long time to run.  PowerShell is neat and all, but you have to launch that process every time and load up those modules.  Sync times and SLAs matter.  Maybe not now, but they will eventually matter.  

    Set the advanced attribute flow.  No need to select any attributes on the data source side.

    

    In the rules extesion, we're setting a binary constant for the binary metaverse attribute logonHours in our source MA within MapAttributesForImport...

                    case "logonHours":
                         byte[] logonhours = {0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0x00};
                         mventry["logonHours"].BinaryValue = logonhours;
                         break;

    Compile then run a full sync.

    Best,

    Jeff Ingalls

    • Proposed as answer by Leo Erlandsson Thursday, January 26, 2017 7:52 AM
    • Marked as answer by Greg Wilkerson Thursday, January 26, 2017 1:09 PM
    Wednesday, January 25, 2017 3:44 AM
  • Additional reasons for rules extension:

    1. Gain benefit of error handling built-in to the ADMA

    2. Gain benefit of operational ease by having errors within sync engine

    2. Gain benefit of retry capability built-in to the ADMA

    3. Maintains integrity each and every time of the AD user account.  If someone were to change the logonHours of an AD account, upon the next sync cycle FIM will fix.

    4. You are stamping the logonHours on the account immediately

    5. You are not relying on PowerShell cmdlets

    Best,

    Jeff Ingalls

    • Proposed as answer by Leo Erlandsson Thursday, January 26, 2017 7:53 AM
    • Marked as answer by Greg Wilkerson Thursday, January 26, 2017 1:09 PM
    Thursday, January 26, 2017 3:32 AM

All replies

  • Let me add the environment. Windows Server 2012R2, running Microsoft Identity Manager 4.3.0.0.
    Tuesday, January 24, 2017 5:03 PM
  • You are going to gain significant performance by using a rules extension.  Even though some customers will panic when they hear the word code, they will also freak out when things take a long time to run.  PowerShell is neat and all, but you have to launch that process every time and load up those modules.  Sync times and SLAs matter.  Maybe not now, but they will eventually matter.  

    Set the advanced attribute flow.  No need to select any attributes on the data source side.

    

    In the rules extesion, we're setting a binary constant for the binary metaverse attribute logonHours in our source MA within MapAttributesForImport...

                    case "logonHours":
                         byte[] logonhours = {0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0x00};
                         mventry["logonHours"].BinaryValue = logonhours;
                         break;

    Compile then run a full sync.

    Best,

    Jeff Ingalls

    • Proposed as answer by Leo Erlandsson Thursday, January 26, 2017 7:52 AM
    • Marked as answer by Greg Wilkerson Thursday, January 26, 2017 1:09 PM
    Wednesday, January 25, 2017 3:44 AM
  • If you must use PowerShell then the MIMWAL docs state you need a wrapper for anything that needs PowerShell 3.0 or above:

    All MIM/FIM workflows run in a .NET Framework 3.5 runtime. This is a product limitation. This .NET runtime environment cannot execute scripts and cmdlets that need PowerShell 3.0 or above runtime. If there is a need to execute a script containing PowerShell 3.0+ cmdlets (e.g. ActiveDirectory module on Windows Server 2012), they can be made to run in a separate process to avoid the product limitation. e.g. using PowerShell Remoting or launching a new "powershell.exe" session using Start-Process cmdlet.

    From: https://github.com/Microsoft/MIMWAL/wiki/Run-PowerShell-Script-Activity

    Wednesday, January 25, 2017 12:18 PM
  • Thanks Jeff,

    But, I'm doing a lot of PowerShell stuff already (creating and securing / destroying home directories, enabling/disabling exchange mailboxes).  Honestly, it doesn't seem to delay things all that much.  We're at about 25,000 users and the entire sync process, which includes updating the database the MAs originally import from takes about 5 minutes.  That's using deltas where possible.

    And, this client simply doesn't have the developer staff. 

    Wednesday, January 25, 2017 9:44 PM
  • Thanks Mark,

    I had read something about having to use a wrapper.  Thanks for the link.  That helped, some. I can now load the ActiveDirectory module.  But...........

    It's throwing an error when executing.  Here is the PowerShell script:

    $AccountName = "myuser"
    $bytLogonHours = new-object byte[] 21
    $bytLogonHours = @(0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,0x00)
    $hashTable = new-object HashTable
    $hashTable.Add("logonHours", $bytLogonHours)
    $command = "&{
    import-module ActiveDirectory
    get-aduser -Identity $AccountName
    set-aduser -Identity $AccountName -replace $hashTable
    pause
    }"
    Start-Process "PowerShell.exe" -ArgumentList "-Version 3.0 -Command $command"

    I get this error:

    Set-ADUser : Cannot bind parameter 'Replace'. Cannot convert the "System.Collections.Hashtable" value of type
    "System.String" to type "System.Collections.Hashtable".
    At line:4 char:40
    + set-aduser -Identity thammond -replace System.Collections.Hashtable
    +                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [Set-ADUser], ParameterBindingException
        + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.ActiveDirectory.Management.Commands.SetADUser

    So, it looks like the hashtable isn't making it into the command as a hashtable, but a string.  Not sure how to fix that.  Still researching it.  My brain is done for the day.  FYI, the pause is in there so I can test it from a PowerShell window.  That's not in the MIMWAL script.

    Wednesday, January 25, 2017 9:51 PM
  • if you are programming in workflow you will need Inlinescript{}

    Workflow test 
    
    {
    
    inlinescript{
    
    write-output "This is where native powershell runs"
    
    ipmo activedirectory
    
    get-aduser
    
    }
    
    }
    

    Or you need to resolve back to native .net as workflow is a .net part.

    You can use native LDAP searchers like I explain on my blog :

    http://www.tech-savvy.nl/2016/12/19/ps-script-search-for-a-object-in-ad-or-ldap-fast-or-from-a-non-domain-joined-pc-with-support-for-workflows-part-1/


    MCTS exchange 2013 | MCTS-MCITP exchange 2010 | MCTS-MCITP Exchange: 2007 | MCSA Messaging: 2003 | MCP windows 2000

    Wednesday, January 25, 2017 10:03 PM
  • Additional reasons for rules extension:

    1. Gain benefit of error handling built-in to the ADMA

    2. Gain benefit of operational ease by having errors within sync engine

    2. Gain benefit of retry capability built-in to the ADMA

    3. Maintains integrity each and every time of the AD user account.  If someone were to change the logonHours of an AD account, upon the next sync cycle FIM will fix.

    4. You are stamping the logonHours on the account immediately

    5. You are not relying on PowerShell cmdlets

    Best,

    Jeff Ingalls

    • Proposed as answer by Leo Erlandsson Thursday, January 26, 2017 7:53 AM
    • Marked as answer by Greg Wilkerson Thursday, January 26, 2017 1:09 PM
    Thursday, January 26, 2017 3:32 AM
  • Jeff, thanks for bringing up #3. That's a deal breaker for me and it completely slipped my mind. With the past IDM system, we had a fair amount of "out of process" activity. MIM has stopped all that.

    That's where I'm going to go with this.  I need to figure out how to get the value in the meta verse from either the Portal or the import authority system.  This will be fun to mess with.  The PowerShell thing was turning into a pain.  I built an Excel spreadsheet that they can use to generate the value.

    Thanks!

    Greg

    Thursday, January 26, 2017 1:09 PM
  • Well, here's what we decided to so. 

    1) Create an AD group with those that needed the logonHours set.  Not a big deal as we needed the group anyway.

    2) Use a group policy to set the logon hours to control the members of that group. 

    No code, relatively easy to manage and it can only be changed in the policy. 

    • Marked as answer by Greg Wilkerson Monday, January 30, 2017 2:58 PM
    • Unmarked as answer by Greg Wilkerson Monday, January 30, 2017 10:01 PM
    Monday, January 30, 2017 2:58 PM
  • Well, this turned out to be a non-event. I created a binary column in the table that the SQL Server MA connects to, added a binary non-indexed logonHours attribute to the metaverse, created an Excel worksheet where a "X' can be put into a cell for an allowed time to log on which generated the binary value, add the logonHours attribute to my AD sync and DB sync rules, populated that value in the table for those that we want to control logon for, ran a sync process and it's done. No code, no hassles, no worries about out of process changes.

    Perfect! 

    Now, I'm trying to figure out why I started this thread in the first place.  Oh well, I get a "DOH!" moment every once in a while.

    Also found out a group policy cannot be assigned to a group.  So, the method above would have never worked, anyway.

    Monday, January 30, 2017 10:08 PM
  • Great solution Greg.  Customer is happy and you've done it in a fully supported and performant way.  Well done.

    Best,

    Jeff Ingalls 

    Wednesday, February 1, 2017 5:21 AM