Showing posts with label Active Directory. Show all posts
Showing posts with label Active Directory. Show all posts

Thursday, April 6, 2017

Domain Discovery - Finding Login Scripts

Background
     With my current contract, I'm helping a company in the process of collapsing multiple domains into one new domain while also tidying up their OU structure, Group Policy Objects, and converting old login scripts to Group Policy Preferences.  A quick query of the SYSVOL and NETLOGON folders show hundreds of login scripts, some dating back to the early 2000's.  Early on, I suspected that we weren't actually using all of them, so here's the process for discovering exactly what you need to concern yourself with. 

The Tools
    All you need to make this work is the ActiveDirectory module. Specifically, we can do almost all of this with the get-aduser command. 


This command pulls a list of all users that are Enabled and have a login script specified. This can take quite a while to run if you have a large domain, but this gives us the data set we need to get started. 


What this does is take just the ScriptPath value, convert it to uppercase (which is important later), and assign those to a new array. 


Now what we're doing is taking the $allScripts array and querying it for unique values. The ToUpper() from earlier is important here because "select -unique" is case sensitive. Had we not converted everything to uppercase, we'd get a ton more unique values because LoginScript.bat, LOGINSCRIPT.bat, and loginscript.bat would all have unique entries.  


This isn't necessary, but it lets you do a quick "does this sound right" check.  In a healthy domain, your count of unique scripts should be very small compared to your amount of users with scripts. 


This piece counts through every unique script we found and tallies up the number of users with that script applied.  From there, you have options for how you want to output it. For example:
$all | sort -property Instances -descending | ft
Will output the array, sorted from highest to lowest number of instances.  This gives you an immediate look at what your most common, and therefore most important, scripts are to migrate over. Alternatively, it can be extremely helpful to see what scripts only have one or two users assigned to them. In the case of my domain, most of those appear to be typos (i.e. LoginScirpt.bat).  
If you need to present a report to your manager, it's a nothing task to run 
$all | export-csv -notypeinformation -path C:\LoginScripts.csv
and fire that off in an email. 

I hope this has been useful. Let me know if there's anything else you'd like to see. 

Tuesday, February 21, 2017

2-21-2017 - Working with CSV Files


Hey everyone, it's been a little while since my last post, but work's been busy.  A recurring issue I see on TechNet is that plenty of people have trouble working with importing/exporting CSV files.  Specifically, I see questions about how to modify CSV files. Personally, I don't see any value in modifying the CSV file directly, but rather importing the data from said file and working with the dataset natively in PowerShell.  Using one person's thread as an example, he had a CSV file full of IP addresses that corresponded to his VM's.  He wanted to add data about the VM based on the IP address, but wanted to know how to "modify the line" in the CSV file.    He had already written part of the script to create the CSV by pinging each computer in his IP range and outputting that to CSV.  For our test, here's what our CSV looks like:
Now, let's say we want to find the hostnames for each of these computers and add that to a new column. There are a couple ways we could do this, but in an enterprise environment, a good way to do it is to just ask AD who it is.   First, we create the empty "column" for our Hostname value.





Once that's created, we need to fill it with values:


















Keep in mind that you will see a lot of red text for any computer that doesn't have an entry in AD. This could be a printer, a switch, etc.  If you're using Windows 8.1+ or Server 2012R2+, you can use the resolve-dnsname cmdlet. 

You can add more columns to the CSV by using the Add-Member cmdlet to your heart's content, and when you're done, you pipe your $dataset variable back to the export-csv cmdlet.   

Really, once you start looking at CSV files as arrays of custom objects, they're pretty easy to work with.

Monday, January 30, 2017

1-30-2017: Using Custom Objects for Fun and Profit

    Custom objects were one of those things I never really saw the point of when I read about them. It wasn't until I actually messed around with them that I really understood their uses. They're especially good for generating reports that pull data from multiple sources.  For example, let's say your manager has requested a report of all the computers in your Accounting department. He wants to know their hostname, their IP Address, their Make/Model, and their Serial Number.  Now, there's no built in function or class (that I know of) that will return all of those pieces of information, so we'll have to pull from multiple data sets.  For this example, we're going to need Active Directory, the Win32_BIOS class, and the Win32_ComputerSystem class. For the sake of argument, let's say we'll also need the Get-CompOU script from the previous post and the hostname of a computer in the Accounting department's OU to get started.
An example asset report script can be downloaded here:
https://goo.gl/5bB03A

Step 1: Generate a list of all the computers needed for this report
$allComps = Get-ADComputer -SearchBase (Get-CompOU AccountingPC1) -filter * -properties * 

      This gives us an array of all the computer objects hosted in the same OU as the Accounting PC we started with. We can get the hostname and IP information just from these objects, but we need more.

Step 2: Enumerate all the computer objects and start data mining
ForEach ($comp in $allComps)
{
        $SWMI = Get-WmiObject -ComputerName $comp.Name -class Win32_ComputerSystem
        $BWMI = Get-WmiObject -ComputerName $comp.name -class Win32_BIOS


We're not quite done with the loop yet, but this shows how we're invoking the WMI classes needed for each computer.  Step 3 takes place within the same loop.

Step 3: Create the custom object to hold our required data
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $comp.Name
$object | Add-Member -MemberType NoteProperty -Name "DNSName" -Value $comp.DNSHostName
$object | Add-Member -MemberType NoteProperty -Name "Serial" -Value $BWMI.SerialNumber
$object | Add-Member -MemberType NoteProperty -Name "IPAddress" -Value $comp.IPV4Address 
 
$object | Add-Member -MemberType NoteProperty -Name "Make" -Value $SWMI.Manufacturer
$object | Add-Member -MemberType NoteProperty -Name "Model" -Value $SWMI.Model 

$allObj += $object
 }
 This one is pretty straight forward. It's a lot of text just to say that you're adding properties to an item you created, assigning those properties values based on multiple classes created in Step 2, and then adding that object to an array of objects.  You can type $allObj = @() at the beginning of your script, but it isn't required.

Step 4: Profit
    At this point, we can dump our report out to a CSV, or we can just output to the screen.  Typing $allObj will just output to the screen, but if we want to make a report our management will be proud of:
$allObj | export-csv -path C:\users\Admin\Documents\AssetReport.csv -NoTypeInformation

And we're done!  Now, if you're working in an environment with SCCM or other management software, these reports might be more easily generated by querying that database. However, this will give you up to the minute accuracy as the WMI queries are done live.  If there's anything you want to see, leave me a comment, and I'll add that to my next post.