Welcome to Powergui.org - an open source community for Windows Powershell

PowerGUI.org PowerGUI.org and blogs

Forums » Active Directory and PowerShell

Thread: Session and Remote Control properties

This question is answered.


Permlink Replies: 20 - Pages: 2 [ 1 2 | Next ] - Last Post: Feb 13, 2008 10:16 AM by: Dmitry Sotnikov
GeorgeK

Posts: 17
Registered: 10/30/07
Session and Remote Control properties
Posted: Oct 30, 2007 7:53 AM
 
  Click to reply to this thread Reply

there are two tabs on the User Property page in AD which I can't get access to from powershell: Session and Remote Control.
I figured out that the settings are kept in the userParameters property but I can’t decode the string it returns:

$k = Get-QADUser -lastname abc
$k.userParameters
P☺CtxCfgPresent????☺CtxCfgFlags1????☺CtxShadow????☺CtxMaxDisco
nnectionTime???☺CtxMaxIdleTime????*☻☺CtxMinEncryptionLevel?

Much less I can think of a way of setting these properties.
Any ideas?




KirkAMunro


Posts: 822
Registered: 3/20/07
Re: Session and Remote Control properties
Posted: Oct 30, 2007 11:00 AM   in response to: GeorgeK
Answered
  Click to reply to this thread Reply

Hi there,

These properties are definitely obscure in Active Directory.  Fortunately you can read and write them in PowerShell without too much effort.  Here is a transcript of a session where I read in a user using Get-QADUser and then I read the EnableRemoteControl and MaxDisconnectionTime values and I finish up by writing the MaxDisconnectionTime value:

PS H:\> $harry = Get-QADUser "Harry Potter"
PS H:\> $harry.DirectoryEntry.PSBase.InvokeGet('EnableRemoteControl')
4
PS H:\> $harry.DirectoryEntry.PSBase.InvokeGet('MaxDisconnectionTime')
60
PS H:\> $harry.DirectoryEntry.PSBase.InvokeSet('MaxDisconnectionTime',120)
PS H:\> $harry.DirectoryEntry.SetInfo()
PS H:\> Remove-Variable harry
PS H:\> $harry = Get-QADUser "Harry Potter"
PS H:\> $harry.DirectoryEntry.PSBase.InvokeGet('MaxDisconnectionTime')
120

The names of the values you are looking for can be found here:
http://msdn2.microsoft.com/en-us/library/Aa380823.aspx

Let me know if you have any other questions.

-
Kirk Munro
Poshoholic
http://poshoholic.com

Kirk Munro [MVP]
Poshoholic

My blog: http://poshoholic.com
Follow me on Twitter: http://twitter.com/poshoholic
KirkAMunro


Posts: 822
Registered: 3/20/07
Re: Session and Remote Control properties
Posted: Oct 30, 2007 11:04 AM   in response to: KirkAMunro
 
  Click to reply to this thread Reply

Hey QAD cmdlet team,

I haven't thought this through in too much detail yet, but wouldn't it be nice if the objects returned by Get-QADUser could be used to discover these properties more easily without jumping through sparsely documented hoops, maybe by providing access to them as members of the QAD user object (hint, hint)?

-
Kirk Munro
Poshoholic
http://poshoholic.com

Kirk Munro [MVP]
Poshoholic

My blog: http://poshoholic.com
Follow me on Twitter: http://twitter.com/poshoholic
GeorgeK

Posts: 17
Registered: 10/30/07
Re: Session and Remote Control properties
Posted: Oct 30, 2007 4:13 PM   in response to: GeorgeK
 
  Click to reply to this thread Reply

Thanks Kirk!

I've been trying to solve this for the longest time.

It would be great to have these properties exposed in the cmdletjust like the easy ones.

Most system admins don’t have developer skills and time toresearch these issues. I enjoy practicing in POSH and getting fairly fluentwith the basics, but I also have tasks to accomplish and at the end of the dayI just want to have access to properties visible in ADUC and filter users with specificsettings. It’s much faster then looking through a few hundred property pages.





GeorgeK

Posts: 17
Registered: 10/30/07
Re: Session and Remote Control properties
Posted: Oct 30, 2007 5:08 PM   in response to: GeorgeK
 
  Click to reply to this thread Reply

Case in the above point:

 If I get the list of users into an array:
>$u = Get-QADUser

 I can get properties like:
>$u[10].name

But can’t call a method:
>$u[10].DirectoryEntry.PSBase.InvokeGet('EnableRemoteControl')

returns:

You cannot call a method on a null-valued expression.At line:1 char:
39+ $u[10].DirectoryEntry.PSBase.InvokeGet( <<<<'EnableRemoteControl')

The same thing happens in a foreach loop.




KirkAMunro


Posts: 822
Registered: 3/20/07
Re: Session and Remote Control properties
Posted: Oct 30, 2007 6:54 PM   in response to: GeorgeK
 
  Click to reply to this thread Reply

Interesting.  It seems that when a collection is returned from Get-QADUser, that the DirectoryEntry attribute is not set on the objects returned (hence the error about null).  Maybe someone on the Quest AD cmdlet team could comment on whether or not this is a bug?

Anyhow, there's more than one way to skin a cat.  I just tested this script locally using a collection and it worked just fine.  Try this:

Get-QADUser | ForEach-Object {
    if ($_.DirectoryEntry -ne $null) {

        $_.DirectoryEntry.PSBase.InvokeGet('EnableRemoteControl')

    } else {

        (New-Object System.DirectoryServices.DirectoryEntry('LDAP://' + $_.DN)).PSBase.InvokeGet('EnableRemoteControl')

    }
}

If there is some more tweaking you would like to do with this, perhaps some specific filtering of objects using a Where-Object cmdlet in the Pipeline or anything else let me know.

Kirk out.



Kirk Munro [MVP]
Poshoholic

My blog: http://poshoholic.com
Follow me on Twitter: http://twitter.com/poshoholic
GeorgeK

Posts: 17
Registered: 10/30/07
Re: Session and Remote Control properties
Posted: Oct 31, 2007 5:52 AM   in response to: KirkAMunro
 
  Click to reply to this thread Reply

This works again, but my cat finds this way of skinning rather painful

What I'm looking for is the ability to filter the list of users by property ('EnableRemoteControl' -eq 2) and pass those user objects on to the pipeline for further processing (create a list of names for reporting, set the property to to a different value etc.)



Andrey Moiseev (Quest)

Posts: 415
Registered: 9/4/07
Re: Session and Remote Control properties
Posted: Oct 31, 2007 7:00 AM   in response to: KirkAMunro
 
  Click to reply to this thread Reply

It's not a bug, just a kind of optimization.

There is much easier way to skin a cat ;-)

Get-QADUser | Get-QADUser | %{$_.DirectoryEntry.<DoSomething>}

By cost of some performance penalty you obtain access to low level features.


Andrey Moiseev (Quest)

Posts: 415
Registered: 9/4/07
Re: Session and Remote Control properties
Posted: Oct 31, 2007 7:05 AM   in response to: Andrey Moiseev ...
 
  Click to reply to this thread Reply

Such way is acceptable for other types of objects as well.
If you want access to low level features for multiple objects  use pattern:

$objects = Get-QADObject <some parameters>
$single_object = $objects[index] | Get-QADObject
$single_object.DirectoryEntry.<Perform low level operation>


KirkAMunro


Posts: 822
Registered: 3/20/07
Re: Session and Remote Control properties
Posted: Oct 31, 2007 10:59 AM   in response to: Andrey Moiseev ...
 
  Click to reply to this thread Reply

Well that last request you made sounded easy enough but it was surprisingly difficult to work out the details.  Anyhow, this script should satisfy your needs by making the advanced attributes directly accessible on the objects you are working with.

## Read in the user objects and add a directory entry member to each of them; this
## member will be initialized to $null
$users = Get-QADUser `
    | Add-Member -Name AddedDirectoryEntry -MemberType NoteProperty -Value $null -Force -PassThru

## Iterate through the users that were returned and set the new member to a valid
## directory entry for each user
foreach ($user in $users) {
    $user.AddedDirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry('LDAP://' + $user.DN)
}

## Add the extended terminal server session and remote control attributes so that
## they can be easily set and retrieved
$extendedProperties = ('TerminalServicesProfilePath','TerminalServicesHomeDirectory','TerminalServicesHomeDrive','AllowLogon','EnableRemoteControl','MaxDisconnectionTime','MaxConnectionTime','MaxIdleTime','ReconnectionAction','BrokenConnectionAction','ConnectClientDrivesAtLogon','ConnectClientPrintersAtLogon','DefaultToMainPrinter','TerminalServicesWorkDirectory','TerminalServicesInitialProgram')
foreach ($extendedProperty in $extendedProperties) {
    $getScriptBlock = Invoke-Expression ('{$this.AddedDirectoryEntry.PSBase.InvokeGet("' + $extendedProperty + '")}')
    $setScriptBlock = Invoke-Expression ('{$this.AddedDirectoryEntry.PSBase.InvokeSet("' + $extendedProperty + '",$args[0]); $this.AddedDirectoryEntry.SetInfo()}')
    $users | Add-Member -Name $extendedProperty -MemberType ScriptProperty -Value $getScriptBlock -SecondValue $setScriptBlock -Force
}

## Now process the objects
$users | Where-Object { $_.EnableRemoteControl -eq 2 } | ForEach-Object {
    $_.EnableRemoteControl = 1
    # Other code can go here
}

Let me know if you still have difficulties with this.

-
Kirk Munro
Poshoholic
http://poshoholic.com



Kirk Munro [MVP]
Poshoholic

My blog: http://poshoholic.com
Follow me on Twitter: http://twitter.com/poshoholic
GeorgeK

Posts: 17
Registered: 10/30/07
Re: Session and Remote Control properties
Posted: Oct 31, 2007 1:57 PM   in response to: KirkAMunro
 
  Click to reply to this thread Reply

Kirk,
great work but the sheer look of it raised the hair on my back and I let my spell checker have its way with Powers Hell…

Andrey’s suggestion looked much easier and I came to the following interpretation of it:

$u = Get-QADUser
$pp = Get-QADObject $u[86]
$pp.DirectoryEntry.PSBase.InvokeGet('EnableRemoteControl')
0

or to get the list:

foreach ($i in $u) {$i.name; (Get-QADObject $i).DirectoryEntry.PSBase.InvokeGet('EnableRemoteControl')}

Still not sure if Andrey  meant something else or made a typo.
Either way, I would never solved this without the two of you.




KirkAMunro


Posts: 822
Registered: 3/20/07
Re: Session and Remote Control properties
Posted: Oct 31, 2007 7:54 PM   in response to: GeorgeK
Helpful
  Click to reply to this thread Reply

I just thought I would fine tune my script a little bit more and incorporate some of the suggestions that Andrey posted earlier directly into the script.  Here's the end result:

## Set the extended properties that we want to retrieve (you can
## remove the properties from this list that you don't need)
$extendedProperties = ('TerminalServicesProfilePath', 'TerminalServicesHomeDirectory', 'TerminalServicesHomeDrive', 'AllowLogon', 'EnableRemoteControl', 'MaxDisconnectionTime', 'MaxConnectionTime', 'MaxIdleTime', 'ReconnectionAction', 'BrokenConnectionAction', 'ConnectClientDrivesAtLogon', 'ConnectClientPrintersAtLogon', 'DefaultToMainPrinter', 'TerminalServicesWorkDirectory', 'TerminalServicesInitialProgram')
 
## Create the extended property accessor script blocks
$extendedPropertyScriptBlocks = @{}
foreach ( $extendedProperty in $extendedProperties ) {
            $extendedPropertyScriptBlocks[$extendedProperty] = @{'get' = '{$this.DirectoryEntry.PSBase.InvokeGet("' + $extendedProperty + '")}';'set' = '{$this.DirectoryEntry.PSBase.InvokeSet("' + $extendedProperty + '",$args[0]); $this.DirectoryEntry.SetInfo()}'}
}
 
## Read the user objects, and iterate through them
Get-QADUser | ForEach-Object {
 
            ## While this might seem redundant, this second Get-QADUser
            ## is important so that the DirectoryEntry member is set on
            ## the user object
            $user = Get-QADUser $_
            
            ## Add a member for each extended property we are going to
            ## be using
            foreach ( $extendedProperty in $extendedProperties ) {
                        $user | Add-Member -Name $extendedProperty -MemberType ScriptProperty -Value (Invoke-Expression ($extendedPropertyScriptBlocks[$extendedProperty].Get)) -SecondValue (Invoke-Expression ($extendedPropertyScriptBlocks[$extendedProperty].Set)) -Force 
            }
 
            ## Example of how you can process the user objects based on
            ## the values of the members that were added
            if ($user.EnableRemoteControl -eq 2) {
                        # Internal processing of users
                        $user.EnableRemoteControl = 1
                        Write-Host "Remote control for user" $user.Name "was reset to" $user.EnableRemoteControl "."
            }
}

This simplifies a little the usage of the objects returned from Get-QADUser while still adding members for any extended properties you care about.  You can remove the extended properties you don't want to use from the assignment of the $extendedProperties array.

The only complicated part to this script are the Add-Member calls, however that complication is necessary if you want to simplify your processing section by accessing extended properties as direct members of the user objects.

Note that sometimes when pasting code into this forum, it inserts spaces after the periods in the code.  So $user.EnableRemoteControl may appear as $user .EnableRemoteControl.  You'll have to remove those spaces for this script to work.

Other than that, you should be able to copy and paste this script into the PowerGUI Script Editor and give it a test run by modifying the first call to Get-QADUser such that it only returns a few users (or just one).

-
Kirk Munro
Poshoholic
http://poshoholic.com

Kirk Munro [MVP]
Poshoholic

My blog: http://poshoholic.com
Follow me on Twitter: http://twitter.com/poshoholic
GeorgeK

Posts: 17
Registered: 10/30/07
Re: Session and Remote Control properties
Posted: Nov 1, 2007 4:47 AM   in response to: KirkAMunro
 
  Click to reply to this thread Reply

Wow, Kirk I finally got your idea!
I'll try to add this as a function to my profile and call it get-KirkADUser, set-KirkADUser.

Seriously, you should write this up as a real cmdlet and create somecompetition to Quest. Kirk AD cmdlets sounds good!

The only reason I’ve been complaining is that I would neverbe able to come up with this on my own. I’m excited about Powershell because ithides the complexity and inconsistency of AD and many other things in theWindows environment. I’m thinking in terms of Users, OUs, Groups etc. not DirectoryEntry.PSBase.InvokeGet.

Nonetheless, this was fun and I’ll try to learn a littlemore of your craft while making this into a function over the weekend.




Dmitry Sotnikov


Posts: 1,151
Registered: 12/1/06
Re: Session and Remote Control properties
Posted: Nov 1, 2007 4:53 AM   in response to: GeorgeK
 
  Click to reply to this thread Reply

Let's hope that competition from Kirk forces AD cmdlets team to add the
functionality natively. ;)

Dmitry


KirkAMunro


Posts: 822
Registered: 3/20/07
Re: Session and Remote Control properties
Posted: Nov 1, 2007 8:42 AM   in response to: Dmitry Sotnikov
 
  Click to reply to this thread Reply

George, I'm really glad to hear that you're starting to understand what I was trying to accomplish in the script.  But I still wasn't happy with having to use such a large script every time you wanted to access the extended properties (perfectionism can be a blessing and a curse ), and I wanted to try something that I hadn't tried with PowerShell before, so I decided to take this a little further.  Well am I ever glad that I did!  I am *so* happy with the results of this that I'll be posting about it on my blog very soon.

Attached to this message is a file called QAD.types.ps1xml.  This file contains instructions on how PowerShell should extend objects of type "Quest.ActiveRoles.ArsPowerShellSnapIn.UserAdsiConnection".  This is the type of the objects returned by Get-QADUser.  It is also the type of the user objects returned by Get-QADObject.  The purpose of the extension is basically to make the extended attributes available as native members of the user object whenever they are retrieved from Get-QADUser or Get-QADObject.  This is nicer than creating a Get-KirkADUser function because as long as you have configured your profile appropriately, you can always call the Get-QADUser cmdlet and you'll get the information you want without having to do anything extra at all.  You never have to worry about modifying scripts to call one cmdlet or the other.  Also, this type extension makes the DirectoryEntry member properly return the appropriate object all the time, even when a set of users is returned by the Get-QADUser cmdlet (no more need to call Get-QADUser twice because of that member being null!).

For example, once you have the QAD.types.ps1xml type data loaded into PowerShell, you could simply do this:

[Edit: I originally was using $this instead of $_. This has been corrected]
Get-QADUser | Where-Object { $_.EnableRemoteControl -eq 2 }  | ForEach-Object {
    # Internal processing of users
    $_. EnableRemoteControl = 1
    Write-Host "Remote control for user" $_.Name "was reset to" $_.EnableRemoteControl "." 
}

All you have to do to set up PowerShell to support this is:

1. Download the attached QAD.types.ps1xml file onto your local machine.
2. Add the following line of script to your profile:
    Update-TypeData fullPathToPs1XmlFileYouDownloaded

For example, on my local machine I placed the file in C:\Program Files\Quest Software\ActiveRoles Server\PowerShell, so I needed to add this to my profile:

[Edit: I forgot to append this path with QAD.types.ps1xml in the original post]
Update-TypeData "C:\Program Files\Quest Software\ActiveRoles Server\PowerShell\QAD.types.ps1xml"

That's it!  Once you have the file downloaded and your profile configured to load the type data, your user objects returned from the Get-QADUser and Get-QADObject cmdlets will have the native members you were looking for!  How cool is that?

I should note that I haven't spent a lot of time testing this, but in the tests that I did run it worked perfectly.  Also note that the ps1xml file I have attached to this post isn't signed, so depending on your execution policy you may have issues loading the type data in it.  That aside, I think you'll be really happy with this solution.

Please let me know if you have any problems with this.  I'd like to work them out before posting this on my blog.

-
Kirk Munro
Poshoholic
http://poshoholic.com

P.S. I just loved your suggestion for me to create Kirk AD cmdlets as competition to Quest!  I work for Quest, but not on the PowerShell projects yet although it is something I have been heading towards.  At the time of this writing, I'm on the MessageStats team.  PowerShell has been my obsession outside of the office for most of this year, and it is likely only a matter of time until I make the switch to work with PowerShell full time.



Kirk Munro [MVP]
Poshoholic

My blog: http://poshoholic.com
Follow me on Twitter: http://twitter.com/poshoholic
Legend
MVP: 2501 + pts
Guru: 2001 - 2500 pts
Expert: 751 - 2000 pts
Enthusiast: 31 - 750 pts
Novice: 0 - 30 pts
Moderators
Helpful answer (5 pts)
Answered (10 pts)

Point your RSS reader here for a feed of the latest messages in all forums