I killed the better part of the weekend trying to upgrade my (first) AppleTV unit from the OEM 40 GB hard drive to a 160 GB drive I had laying around.  And while there are a lot of directions on how to do it, none of them quite worked for me.  I got all sorts of errors at various stages of the upgrades and partition creations, which led me to write this up.  Plus, I’m putting this together for me as a concise guide for when I upgrade my second unit.

What you need:

  1. AppleTV unit
  2. Torx key – size 8 – sorry, but they used a torx key instead of an allen key or phillips screws
  3. An Apple Mac running OS X or later
  4. Some sort of connector/converter to plug your IDE drives into your Mac (preferrably via USB)
  5. new 2.5″ E-IDE hard drive

For #2, I got one of these from Micro Center:

http://www.microcenter.com/single_product_results.phtml?product_id=0296591

(it was cheap, everything stored in the handle, and it was green – I like green)

For #4, I got this thing, also from Micro Center:
http://www.microcenter.com/single_product_results.phtml?product_id=0278844
(I’m considering returning it and getting one for $5 more that also handles SATA drives but don’t think I need it)

Now, for taking the unit apart, just have a look here:
http://www.macworld.com/article/57079/2007/03/appletvharddrive.html

These are the initial directions I started with, but to copy the entire, 40 GB partition (where 36 GB of it can be pushed back out again after the install) took a huge amount of time (about 3 hours on a 2.6 Ghz MacBook Pro).  Plus, at the very end, I started getting error messages when trying to up-size the 4th (Media) partition.

So after noodling around with this for half the day, I found a shorter set of directions here:
http://www.engadget.com/2007/03/23/how-to-upgrade-the-drive-in-your-apple-tv/

However, they don’t tell you how to restore the 3rd partition properly, and my AppleTV just wouldn’t start up after I did it.  I really think its because the 3rd volume has to be called OSBoot but who knows.  I didn’t want to ever open up the unit ever again, so I was going to do it right from beginning to end with no errors.  Once again, I started from scratch combining what I’d learned and came up with the script outlined below.  This script has you copying the first 3 partitions, and then creating the 4th partition with the remaining space.

First off, lets get the old 40 GB drive out of the AppleTV and hook it up to your Mac.  Then, open up Terminal and run these commands:

(Everything below assumes the drive is your second drive, disk1 – if its not, then replace the paths accordingly)

# might as well go in and do all this as root - its just too time consuming not to
sudo bash

# copies the entire disk - this is 'Plan B' and not used again
dd if=/dev/disk1 of=/Users/AppleTV-full.dmg bs=1024k

# copies just the 3 necessary partitions
dd if=/dev/disk1s1 of=/Users/AppleTV-efi.dmg bs=1m
dd if=/dev/disk1s2 of=/Users/AppleTV-recovery.dmg bs=1m
dd if=/dev/disk1s3 of=/Users/AppleTV-OSBoot.dmg bs=1m

# remove original ATV disk
diskutil eject disk1

Ok.  You now have what you need from the old disk, but don’t format it or throw it in the bin just yet.  Make sure everything works before doing anything else with it.  Also, you’ll notice the first line makes a full backup.  Yes, this takes a huge amount of time, but is worth doing in case something happens, and you need to restore the full thing.  I haven’t had to use it, but who knows – its nice to have, and 40 GB is pretty easy to keep around these days.

Onto the new drive, now.  This bit took forever to get right, and figure out the quickest way to do this, but it is the only one that didn’t throw ‘gpt bogus map’ errors, unmount unexpected when trying to format the last partition or give me ‘disk not available’ messages.  I hope it helps someone out there:

# -----------------------------------------------
# partition by partition restore
# -----------------------------------------------
diskutil unmountDisk /dev/disk1
gpt remove -a /dev/disk1
gpt destroy /dev/disk1
gpt create /dev/disk1

# EFI Partition
diskutil unmountDisk /dev/disk1
gpt add -b 40 -i 1 -s 69632 -t efi /dev/disk1
dd if=/Users/AppleTV-efi.dmg of=/dev/disk1s1 bs=1m

# Apple_Recovery Partition
diskutil unmountDisk /dev/disk1
gpt add -b 69672 -i 2 -s 819200 -t 5265636F-7665-11AA-AA11-00306543ECAC /dev/disk1
dd if=/Users/AppleTV-recovery.dmg of=/dev/disk1s2 bs=1m

# OSBoot Partition
diskutil unmountDisk /dev/disk1
gpt add -b 888872 -i 3 -s 1843200 -t hfs /dev/disk1
dd if=/Users/AppleTV-OSBoot.dmg of=/dev/disk1s3 bs=1m

diskutil unmountDisk /dev/disk1
gpt show /dev/disk1

# get the starting value for the 4th partition from the last command and
# replace it for 2732072 highlighted in green below - this is the starting
# point for the 4th partition and will be specific to the drive you're using
diskutil unmountDisk /dev/disk1
gpt add -b 2732072 -i 4 -t hfs /dev/disk1

diskutil unmountDisk /dev/disk1
newfs_hfs -J -v Media /dev/disk1s4

And that’s it.  Put it all back together, plug it in, and wait a good minute for it to boot up.  Having done that, you’re all set.  Good luck . . .

(this post was originally started on 21st Oct 2009 whilst I was still in the UK).

I’m heading home from a good client meeting about my company’s AD Bridge product (QAS – pronounced Vintela) and am still surprised at the politics involved in IDM, even after everyone has agreed something needs to be done for audit/compliance reasons and the product is fit for purpose. My colleagues and I were there to help with technical and commercial questions but there’s nothing we do for the politics.

Unix Engineering, AD Architecture, InfoSec, End-User Services and the PMO were well represented and from all regions.  And I think that’s the big problem with IDM in general.  This is a common scenario, and I often have 3-5 different groups represented in my meetings. Having worked in Dev, DB, Infrastructure, Support and other IT functions, I’ve never gotten involved in anything that included all these groups at once.  Usually, some group was left out, which made things easier.  But with IDM, everyone is involved and stays engaged throughout the project.

And, of course, each group wants the project to succeed but everyone also realised that territories were getting re-arranged. So each lobbied for things to make their part easier or better. On the one hand, I can see every person’s position having done all of those jobs in the past. But if I were an exec, I’d try to figure out a way to have more cooperation and less posturing.  When it comes to IT processes, an IDM project is going to quickly uncover everyone’s motives and show the unlying politics which may seem dormant or non-existent with other projects.

And so it goes . . . with all large projects, its the people, and not the process or tools, that complicate things.

As an aside, when I managed folks and a re-org was needed, I’ve often (half jokingly) asked who would you put where if you “fired” everyone and started over. Not for real, of course, and you can have all the same people, but would you put them in different roles? Would you let anyone go?  Are there any roles that are missing?  What about necessary people in unnecessary roles?

A long time, when dotcoms rules the land, I was a Dev Manager for www.eTour.com (don’t bother following the link – they’re gone). In those days, we were still trying to introduce people to the web and there was a buzz in the industry that I hadn’t seen before or since. And one that will probably never occur again.

Well, one of the funniest, yet instructional moments came during our death throes. The conversation with the Product Manager went something like this:

Brian: Dmitry, check this out – we need to do this one the site.

Dmitry (walking over): BC, put that site away – this is the workplace – go look at that stuff at home.

BC: no, look at this (closes browser window). See that? There’s another window under it.

DK: yeah, ok. A porn site with pop-ups – what’s your point?

BC: No – check it out.  They manage to put the pop-up under the main window.  Watch again.

DK: OK – I see.  Definitely interesting . . . send me the URL and we’ll figure it out.

Plain and simple, they came up with the ‘pop under’ which was much less obnoxious than the ‘pop-up’ that was so prevalent at the time.  I then had to go to one of our (female) developers and ask her to reverse engineer how it was done.  It turns out it was a simple call to a window.blur() function.

Its interesting that porn (followed by gambling) lead innovation in terms of technology.  It certainly was the only thing making money at the time of the dotcom meltdown and a lot of the things you now see with streaming video (a la YouTube) was not developed by Google or some other well-known company, but by those in the ’seedy part’ of the internet.

What does this have to do with ‘Identity Management?’ I’m not sure, but I’m willing to bet they will continue to innovate, and it will have some sort of impact on the rest of the computing field.

I spent the better part of a week in October in Luxembourg, and most of this post was written on October 21st, 2009.  However, its been sitting in my Drafts queue this whole time.

In any case, it was about what I expected.  It was a cute little city/country nestled inside of Europe with a good mix of people from various other countries.  The one thing that did truly surprise me was a Greek developer name Michael P.  What surprised me most was that he was able to take Quest VSJ and get it running in about 30 minutes, with minimal instructions, and having a machine on a different domain, with no DNS resolution to the AD domain where VSJ was installed.

Now, I should point out that Barry G and I have gone out of our way to make VSJ accessible to customers trying it out the first time, but its still a hard product for developers to truly ‘grok.’  And the product itself is not difficult but the whole notion of ‘Java using Microsoft for authentication and authorisation’ seems to really trouble some people.  Even though its really a code library, like many others, and is often used as a servlet filter, like many others, developers get really hung up with the fact that its working with Active Directory.  Its some sort of stumbling block for them, even though it behaves like any other piece of Java code.

But Mike just nodded when I told him it was a servlet filter, and grabbed the USB stick from me with the binaries.  One of a few developers I’ve met in a long time that didn’t subscribe to any sort of ‘religion.’

Within 10 minutes, he came back saying he was getting licensing errors!  Perfect – it was ‘as expected’ as I forgot to give him a file called vsj-license.jar and he disappeared when I did.  About 5 minutes after that, he came back with ‘access denied’ error messages, which meant that he installed it, and it was behaving ‘as expected.’

Some things Mike saw when he first tried to log in turn into good tips to keep in mind if you’re actually deploying or testing VSJ:

  • Kerberos didn’t work, which was expected, so he failed to login.  This was not a surprise, as VSJ defaults to leaving NTLM and Basic Auth turned off.  Good settings for a production environment, but one that trips people up when they first using VSJ in a development and test environment.  So he checked the vsj.properties file and turned both options on for testing.
  • Internet Explorer is an abysmal browser.  Once it starts doing something (like NTLM) there is no way to get it to stop.  Even turning NTLM off in the servlet filter doesn’t stop it from trying.  Nothing short of a reboot stops it from behaving this way.  So we enabled SP-NEGO in Firefox and continued on.  We also turned off NTLM, but left Basic Auth on, which then has the servlet filter requesting the Kerberos ticket on the user’s behalf.
  • Mike P was not in the test domain, so Kerberos would never work for him off his machine.  Unless . . . he used the “runas” command.  But even runas doesn’t work well off machines that are on another domain.  However, we used the “runas /netonly” command which gets past that.
  • Finally, we did reconfigure Mike’s machine to use the test domain’s DNS.  There is no way to do proper Kerberos without good name resolution, and we needed to get host and SRV records to get a ticket onto Mike’s machine.

There are definitely a lot more things to keep in mind when working with VSJ.  If you do need help, check out these forums or call your Quest rep to get you in touch with some knowledgable people.

One thing that has come up a lot recently is how to provision Oracle DB users from ActiveRoles Server.  Oracle DB users are not very hard to create but most people using ARS have little to no experience with managing DB users.  And, unlike applications, DB users can’t simply be inserted into a table as tables like SYSUSER may have dependencies to other parts of the DB.

There are a lot of different ways to do this, but below is an outline of something I wrote almost 2 years ago, and is still applicable today.  Note: this is all in VB Script and is compatible with any version of ARS 6.x.  It may even work with 5.x.  I do have plans on converting this to PowerShell for use in 6.5, but this ought to be enough to get you moving.

Start off with a script library called “DB Code.”   The idea is to have all the connection and DB execution code in one place, and then decide which DB code (the actual SQL commands) gets called into the ARS event handlers.  Here is that code with a hard-coded connection string.

Option Explicit

' ************************************************************
' This function executes any SQL command sent to it against
' the Oracle DB
' ************************************************************
Function ExecuteSQLScript(p_sSQLToExecute)
 Dim oFS
 Dim oFSFile
 Dim strConnectionString
 Dim oConnection
 Dim oCmd

 Const cnstCommand = 1 'Command type - 1 is for standard query
 Const ForReading = 1
 Const ForWriting = 2
 Const ForAppending = 8

 ' sample connection string from www.connectionstrings.com
 ' Driver={Microsoft ODBC For Oracle};Server=myServerAddress;Uid=myUsername;Pwd=myPassword;

 ' connection string - only server name and DB name should change
 strConnectionString = "Driver={Microsoft ODBC for Oracle};Server=OraDB1;Uid=SYSTEM;Pwd=Password1;"

' uncomment next 3 lines for debugging
 On Error Resume Next
 Set oFS = CreateObject("Scripting.FileSystemObject")
 Set oFSFile = oFS.OpenTextFile("C:\\Log.txt", ForAppending , False) 

 ' connect to oracle db
 Set oConnection = CreateObject("ADODB.Connection") 

 'Open connection using ConectionString
 oConnection.Open strConnectionString 

 Set oCmd = CreateObject("ADODB.Command")

 ' prepare SQL Statement

 ' create the user to be external
 oCmd.CommandText = p_sSQLToExecute
 oCmd.CommandType = cnstCommand
 oCmd.ActiveConnection = oConnection
 oCmd.Execute

 ' uncomment next line for debugging and put them right where you think the error is happening
 oFSFile.WriteLine(p_sSQLToExecute & " *** Error: " & Err.Number & " " & Err.Description)

 ' close the connection and disconnect from oracle db
 Set oCmd = Nothing

 oConnection.Close
 Set oConnection = Nothing

' uncomment next 3 lines for debugging
 oFSFile.Close
 Set oFSFile = Nothing
 Set oFS = Nothing
End Function

Note that I’ve got some debugging coded in there, and this will write every SQL command sent to a file called c:\Log.txt.  You may wish to turn this off in production.  Also, this script is meant to go into “Script Modules/QSC Scripts” and is called “DB Code”.  If you alter any of this, you’ll need to change the objLib references below.

Next, you simply create some script policies that have event handlers which will execute this code.  My suggestion is to use the OnPost set of events so that if something happens, the rest of the action can continue. The first bit of sample code is for creating the account in Oracle.  Now, keep in mind that creating a user doesn’t do anything until you GRANT the user some rights.  So its OK to create users since they cannot connect up and do anything just yet.

Option Explicit

Sub onPostCreate(Request)
 Dim strsAMAccountName
 Dim strExecuteSQL
 Dim strTitle
 Dim objLib

 Set objLib = ScriptLib.Load("Script Modules/QSC Scripts/DB Code")

 Request.GetInfo
 strsAMAccountName = UCase(Request.Get("samaccountname"))
 strTitle = UCase(Request.Get("title"))

 ' if this change is not for a user, get out
 If (LCase(Request.Class) <> LCase("user")) Then Exit Sub    

 ' prepare SQL Statement

 ' write the new account into the table
 strExecuteSQL = " CREATE USER " & strsAMAccountName & " IDENTIFIED BY Password1 "

 Call objLib.ExecuteSQLScript(strExecuteSQL)

 'grant access if the new account is a production dba
 If strTitle = "PRODUCTION DBA" Then
 strExecuteSQL = " GRANT CONNECT TO " & strsAMAccountName & " "
 Call objLib.ExecuteSQLScript(strExecuteSQL)
 End If 

End Sub

Next, we have an example that shows a simple grant/revoke command based on someone’s job title (if the user is a Production DBA he gets connect access while anyone else is revoked).

Option Explicit

Sub onPostModify(Request)
 Dim strsAMAccountName
 Dim strExecuteSQL
 Dim objLib
 Dim strTitle

 Set objLib = ScriptLib.Load("Script Modules/QSC Scripts/DB Code")

 Dim objObj
 On Error Resume Next
 If (DirObj Is Nothing) Then
    Set objObj = Request
 Else
    Set objObj = DirObj
 End If
 On Error GoTo 0

 ' if this change is not for a user, get out
 If (LCase(objObj.Class) <> LCase("user")) Then Exit Sub    

 strsAMAccountName = UCase(objObj.Get("samaccountname"))
 strTitle = UCase(objObj.Get("title"))
 strEmpStatus = UCase(objObj.Get("edsvaEmpStatus"))

 ' prepare SQL Statement

 ' If you are DBA you should have connect rights
 If strTitle = "PRODUCTION DBA" Then
 ' write the new account into the table
 strExecuteSQL = " GRANT CONNECT TO " & strsAMAccountName & " IDENTIFIED BY Password1"

 Else
 ' delete the account from the table
 strExecuteSQL = " REVOKE CONNECT FROM """ & strsAMAccountName & " "
 End If

 Call objLib.ExecuteSQLScript(strExecuteSQL)

 If strEmpStatus = "TERMINATED" Then
 objObj.Put "edsvaDeprovisionType", 1
 objObj.SetInfo
 End If

End Sub

Finally, here’s an example of what you can do when someone is deprovisioned:

Option Explicit

Sub onPreDeprovision(Request)
 Dim strsAMAccountName
 Dim strExecuteSQL
 Dim objLib

 Set objLib = ScriptLib.Load("Script Modules/QSC Scripts/DB Code")

 ' if this change is not for a user, get out
 If (LCase(DirObj.Class) <> LCase("user")) Then Exit Sub    

 strsAMAccountName = UCase(DirObj.Get("samaccountname"))

 ' prepare SQL Statement

 ' delete the account from the table
 strExecuteSQL = " DROP USER " & strsAMAccountName & "" & " CASCADE "

 Call objLib.ExecuteSQLScript(strExecuteSQL)
End Sub

At the end of it all, all I’m doing is calling SQL statements that a DBA would use in creating and managing users within Oracle.  This same approach will actually work with MySQL, SQL Server, DB2 and most other RDBMS provided the correct syntax.  And, for example, if you want to get really clever, you could create AD groups that are analogous to Oracle Roles, like SYSDBA,

I would suggest you be careful with that last one as the CASCADE part of the command will drop any objects owned by the user within Oracle, and you could lose data.  Be sure to talk to the Oracle DBAs first, and walk them through what you’re doing.  And, as always, this post is made available with no guarantees, assurances, promises or commitments.  Your mileage may vary, and you really should contact Quest Professional Services if you need assistance with ActiveRoles Server.

I spent some time with a manufacturing client this week, and did quite a bit of hands on work with ActiveRoles Server.  The guy I worked with was quite good, “grokked” exactly how the product worked, and all the features it had.  However, there’s quite a lot there, and if you don’t spend your time in it day in and day out, you’ll probably forget some key points.  At the time, I made a note that I need to record some additional videos to show some of the functionality we reviewed.  This would help him later on as a refresher without having to schedule myself or someone else to walk him through it again.

Of course, when I think to do these things, other tasks take over, and I never get to them.  However, as luck would have it, I got an internal email on the way home.  The email was asking for help with showing granular delegation through QARS.  So here is my 6 minute, really quick and off-the-cuff recording (this one has audio).  Enjoy:

http://www.idmwizard.com/quest/QARS6.1GranularDelegation/QARS6.1GranularDelegation.html

Hopefully, I’ll be able to find the time to post more of these recordings . . . or coerce someone else into doing a few as well.

I had a client a long time ago (in 2007) ask if they can have a way to never re-use an account name.  They were looking at Quest ActiveRoles Server (google for it), and this was a key requirement.  Well, this would be very easy to do with the built-in policies if they kept their disabled users around.  However, they didn’t want to clutter AD with similar account names, and disabled accounts.  Which meant that QARS wouldn’t be able to check AD for uniqueness as the accounts would be wiped out.  I initially suggested they use an ADAM (now called AD LDS) store for this, and have AD include it in the scope.  However, but they thought it was too cumbersome for this task (and, honestly, it was).

So I had to come up with a scripted solution to get past this hurdle and still provide them a way to create unique names into perpetuity without leaving objects in AD or ADAM.  This question has come up again internally, so I thought it would make sense to publish this to the rest of the world for future reference. First is a recording of how to install the bits and show you how it works.  The short version is that it:

a. creates a table in the QARS database to keep track of every user name created as the account is being provisioned.
b. it installs a policy that checks the table from part (a) and generates a new user name based on the previous names in the DB.

The way the script is written (and this is what the client wanted), they wanted to create a user with first name, then last initial.  If that was taken, use the next 2 letters, 3 letters, and so on.  At some point, you run out of options, and have to resort to numbering.  Obviously, the script needs to be modified to meet your needs, and there are some great Professional Services people at Quest to help if you need it, but perhaps this sample is enough.

This post, as with all others, implies no warranty, and I do NOT support this solution (unless you wish to pay me) and is posted as an example of what is possible with Quest ActiveRoles Server.  If you have questions, please contact your Quest account manager about what support options are available.

Now . . . without further ado, here are the links you want.  First, here is a recorded video of the installation and usage:

http://www.idmwizard.com/quest//UniqueUserID/index.html

And here is the zip file shown in the video (albeit renamed – but you should be able to figure it out):
http://www.idmwizard.com/quest/UniqueUserID/UniqueUserID_policy.zip

Cheers,
Dmitry

(note: edited 2009-09-22 – changed some text and updated links to open in new windows and work properly).

With the release of VAS 3.5 last year, there’s been a marked increase in Mac interest and activity for me.  One thing I had to do whilst on-site with an Italian client was give them a way to deploy VAS to 300+ Macs without visiting each machine.  Basically, they needed a scripted way to install an mpkg or dmg file onto the Mac.  In the unix and linux world, this is pretty common.  All of the major vendors have clear documentation on how to do this.

However, Apple’s approach is always through the GUI.  And finding an example on how to do this from the command line took quite some time.  So to save someone the trouble in the future, here is the script I sent over to the client.  Since writing it in March, I’ve had at least a half dozen requests for it inside of Quest, so it made sense to put this out there publicly.  And while this one is specific to VAS (extra bonus if you’re running VAS on Mac), it should work for most Mac packages, and should only require a minor tweaking.

Note: the only requirement is that some sort of remote login option be available – there’s simply no point to using this script if you’re going to sit in front of a Mac inside the terminal window.  The way to do this is to enable ‘Remote Login,’ which is off by default, and that will enable ssh on the mac so you can connect to it with something like Putty.

As an added bonus, here’s a 6 minute video showing this being done: http://www.idmwizard.com/quest/vas35_mac_install_manual/index.html

########################################################################
# install the mac client using the command line
# first, mount the dmg file
hdiutil attach /<somelocation>/VAS-3.5.0.33.dmg

# that should create a new volume which we can cd to
cd /Volumes/VAS-Installer 
# this is the actual install of VAS onto the machine
sudo /usr/sbin/installer -pkg VAS.mpkg/ -target / 
# install is done, so we can now unmount the dmg - change directories first, though!
cd /opt/quest/bin
hdiutil detach /Volumes/VAS-Installer 
# join the machine to the AD domain
# sudo /opt/quest/bin/vastool -u <aduser> join -c "ou=apple,ou=xxx,ou=yyyy,dc=root,dc=dom" root.dom 
# better yet, join the machine with a pre-created account
# HOST=`hostname | awk -F. '{print $1}'`; /opt/quest/bin/vastool -u host/ -w $HOST join -f -n ${HOST}.root.dom root.dom
# update DNS record in AD (DDNS is in the VAS package install)
# but if your mac is not using dhcp, I don't think this is run
sudo /opt/quest/sbin/dnsupdate <IP> 
# since macs are 'personal'
# there's usually 1 user on the machine - and you probably already have
# 1 AD user ready to use
# so copy the default user to the new AD user
# (this may take some time depending on the folder size)
sudo cp -R /Users/<localuser> /Users/<ADUser>
# reown all the files to the AD users (<ADGroup> can also be a local group)
sudo chown -R <ADUser>:<ADGroup> /Users/<ADUser> 
# later, when everyone is happy, and it is all working, run this to get rid of the local user profile
sudo rm -rf /Users/<localuser>

I found this rattling around in my email from July 16th.  Its a quick 1 liner to add every unregistered GPO into GPM:

foreach($unregObj in ($VCManager.GetGpos($VCManager.GetDomainNames())  |
   Where-Object -FilterScript {$_.Registered -eq $False} ))
   {$VCManager.Register("00000000-0000-0000-0000-000000000000", $unregObj,
   "Registering automatically via PoSh script", 1.0);}

You do need to prefix this with something like:

& 'C:\Program Files\Quest Software\Quest Group Policy Manager\QGPMInit.ps1' -computerName localhost -portNumber 40200

but that’s what happens when there’s no PoSh cmdlets available. Hope it helps . . .

As an interlude from the GPM PowerShell I’d been doing, Stuart Harrison asked for some PowerShell samples for Defender.  Stu is the Product Manager for Quest Defender, and can be found here:
     http://stuharrison.blogspot.com 
here:
     http://twitter.com/s7uart
and here:
     http://www.linkedin.com/pub/stuart-harrison/1/197/a61

I planned on spending a good chunk of the day writing some stuff for him, but one thing led to another, and other work intruded.  However, I did just manage to eek out the following 3 scripts as starters for him to start using.  All 3 scripts (can they really be called scripts when they’re this short?) rely on the AD cmdlets that you can find at http://www.quest.com/free-tools (its the second entry down called “ActiveRoles Management Shell for Active Directory”). 

This first one liner answers the question, “Which tokens has which users assigned?”

foreach ($token in (Get-QADObject -IncludeAllProperties -Type "defender-tokenClass"))
{ Write-Output $token.Name $token."defender-tokenUsersDNs" }

This next one is the same as the above one, but gets user attributes as well, so you can now see violation counts, etc:

foreach ($token in (Get-QADObject -IncludeAllProperties -Type "defender-tokenClass"))
      {
            Write-Output $token.Name;
            foreach ($user in $token."defender-TokenUsersDNs" | Where-Object{$token."defender-TokenUsersDNs".Count -gt 0})
            {
                  Get-QADUser -Identity $user -IncludeAllProperties | Format-List samAccountName, "defender-violationCount",
                      "defender-resetCount", "defender-lockoutTime" ;
            }
      }

This last one is around DANs (Defender Access Nodes) and looks to answer “Who can log in based on (direct) group membership allowed on the DAN?”:

foreach ($dan in Get-QADObject -Type "defender-danClass" -IncludeAllProperties)
{
      write-output "---";
      Write-Output $dan.Name;
      foreach ($ADGroup in Get-QADGroup -Identity $dan."defender-DANMembers")
      {
            Write-Host $ADGroup;
            $ADGroup.Members | Get-QADUser | Format-List sAMAccountName;
      }
}

Hopefully, this is enough to get some people started on writing some PoSh scripts for Defender and I hope to post some more up soon.