Powershell - Store all Computers in AD into Array - powershell

So at the moment I have some code which can lock down to a specific OU via ADSI in Powershell, loop through, and store them into an Array. In turn I loop through this and run a Test-Connection. I have my reasons...
Anyway, is it possible (using only inbuilt cmdlets, i.e. no Quest stuff) to recurse through the whole of AD and add all Computer Objects to the array?
$myArrayOfComputers = #()
$orgUnit = [ADSI]"LDAP://OU=foo,DC=foo,dc=co,dc=uk"
ForEach($child in $orgUnit.psbase.Children) {
if ($child.ObjectCategory -like '*computer*') { $myArrayOfComputers += $child.Name }
}
ForEach($i in $myArrayOfComputers) {
Test-Connection $i
}

In PowerShell V2.0 you can try :
Import-module ActiveDirectory
$computers = Get-ADComputer *
In PowerShell V1.0 You can try :
# dom.fr is the DNS root name of the domain
$dn = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://dom.fr:389/dc=dom,dc=fr","administrator#dom.fr","admin")
# Look for computers
$Rech = new-object System.DirectoryServices.DirectorySearcher($dn)
$Rech.filter = "((objectClass=computer))"
$Rech.SearchScope = "subtree"
$Rech.PropertiesToLoad.Add("sAMAccountName");
$Rech.PropertiesToLoad.Add("lastLogon");
$Rech.PropertiesToLoad.Add("distinguishedname");
$computers = $Rech.findall()

On V2 using .net:
Add-Type -AssemblyName System.DirectoryServices.AccountManagement | out-null
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$pc = new-object 'System.DirectoryServices.AccountManagement.PrincipalContext'($ct, "foo.co.uk", "OU=foo,DC=foo,dc=co,dc=uk");
$cpp = New-Object 'System.DirectoryServices.AccountManagement.Computerprincipal'($pc)
$ps = new-object 'System.DirectoryServices.AccountManagement.PrincipalSearcher'
$ps.QueryFilter = $cpp
$MyListArray = $ps.FindAll() | select -expa name

Related

Powershell DirectorySearcher Null Output

I'm writing a powershell script that searches for users inside an Active Directory OU and allows me to reset passwords by choosing matches from a list. I found a Tutorial that uses the System.DirectoryServices.DirectoryEntry and System.DirectoryServices.DirectorySearcher, and modified it like so:
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP:\\[REDACTED]")
##ReadSTDIN
$strSearch = Read-Host -Prompt "Search"
$strCat = "(&(objectCategory=User)(Name=*" + $strSearch + "*))"
## Search Object
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strCat
$objSearcher.SearchScope = "Subtree"
#Load Required Properties into the dynObjLink
$objSearcher.PropertiesToLoad.Add("name")
$objSearcher.PropertiesToLoad.Add("userPrincipalName")
$objSearcher.PropertiesToLoad.Add("SamAccountName")
##Magical Search Function
$colResults = $objSearcher.FindAll()
$colResults.PropertiesLoaded
#for every returned userID add them to a table
ForEach ($objResult in $colResults)
{$a++
$objResult.count
$objItem = $objResult.Properties
$objItem.name
$objItem.userPrincipalName
$results.Add($a, $objItem.name + $objItem.userPrincipalName + $objItem.SamAccountName)
}
#Print Table
$results | Format-Table -AutoSize
This works well enough, but when it prints data I can only get the "first name" value of anything that comes back. Everything else becomes NULL and I can't figure out why.
Name Value
---- -----
3 {James3 [REDACTED], $null, $null}
2 {James2 [REDACTED], $null, $null}
1 {James1 [REDACTED], $null, $null}
I've tried different kinds of authentication and manipulating values, but the DirectorySearcher object only seems to collect the "name" value of any record it returns, no matter what I load into it. Help?
Here's a bit shorter (and PowerShell v2-compatible) way of doing this:
#requires -version 2
param(
[Parameter(Mandatory=$true)]
[String] $SearchPattern
)
$searcher = [ADSISearcher] "(&(objectClass=user)(name=$SearchPattern))"
$searcher.PageSize = 1000
$searcher.PropertiesToLoad.AddRange(#("name","samAccountName","userPrincipalName"))
$searchResults = $searcher.FindAll()
if ( $searchResults.Count -gt 0 ) {
foreach ( $searchResult in $searchResults ) {
$properties = $searchResult.Properties
$searchResult | Select-Object `
#{Name = "name"; Expression = {$properties["name"][0]}},
#{Name = "sAMAccountName"; Expression = {$properties["samaccountname"][0]}},
#{Name = "userPrincipalName"; Expression = {$properties["userprincipalname"][0]}}
}
}
$searchResults.Dispose()
Note that there's no need to build a list and output afterwards. Just output each search result. Put this code in a script file and call it:
PS C:\Scripts> .\Searcher.ps1 "*dyer*"
If you omit the parameter, PowerShell will prompt you for it (because the parameter is marked as mandatory).
try using Properties matching to the PropertiesToLoad
$entry = new-object -typename system.directoryservices.directoryentry -ArgumentList $LDAPServer, "ldap", "esildap"
$entry.Path="LDAP://OU=childOU,OU=parentOU,DC=dc1,DC=dc2"
$searcher = new-object -typename system.directoryservices.directorysearcher -ArgumentList $entry
$searcher.PropertiesToLoad.Add('samaccountname')
$searcher.PropertiesToLoad.Add('mail')
$searcher.PropertiesToLoad.Add('displayname')
$objs = $searcher.findall()
foreach($data in $objs)
{
$samaccountname = $data.properties['samaccountname'][0] + ''
$mail = $data.properties['mail'][0] + ''
$displayname = $data.properties['displayname'][0] + ''
}
when accessing the properties of the resultset you get a System.DirectoryServices.ResultPropertyValueCollection type for each property
to get a string value for passing to a database the property value access the zero index of the object

Some computers on a powershell GPO script not recognizing parameter

I am running into an issue where about 10% of computers on my network are throwing a very strange errors when processing. The error I get is "Where-Object : A Parameter cannot be found that matches paramter name 'Property'" the code I'm using is as follows.
#Create ADSI Search object to query Active Directory for usernames
#Start-Transcript -Path "$env:userprofile\Desktop\log.txt"
$strFilter = "objectCategory=user"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://OU=SD25;DC=DC;DC=DC")
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 100000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"
#Populate ADSI with the extra fields of samaccountname which is the username, and memberof which gives you roughly which groups they are a memberof
$colProplist = "samaccountname", "memberof"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
#Run the Search
$colResults = $objSearcher.FindAll()
#$colResults
$resultsarray = #()
#The way ADSI returns results, it populates all an array of every username listed within the scope, I then use this foreach recursive loop to find the name I need
foreach ($objResult in $colResults)
{
#Here I am taking each of the users, and finding the one which has the samaccountname of the user that is currently logged in
$objItem = $objResult.Properties | Where-Object -Property memberof -like ALL
#$groups = $objItem.memberof
#This is for diagnostics, if you output a logfile it will tell you the name and groups it is a member of
$objitem
}
#This is the beginnings of searching for a computer container in active directory.
$compFilter = "objectCategory=computer"
$compDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://OU=OU;DC=DC;DC=DC")
$compSearcher = New-Object System.DirectoryServices.DirectorySearcher
$compSearcher.SearchRoot = $objDomain
$compSearcher.PageSize = 100000
$compSearcher.Filter = $strFilter
$compSearcher.SearchScope = "Subtree"
$compProplist = "name"
foreach ($i in $compPropList){$compSearcher.PropertiesToLoad.Add($i)}
$compResults = $compSearcher.FindAll()
foreach ($compR in $compResults)
{
}
#Stop-Transcript
IIRC -Property was introduced to Where-Object with PowerShell 3.0. Could you script be running on PowerShell 2.0?
Responding to comment
You need to create a filter script in the form of a scriptblock (i.e. PowerShell code in a set of braces) instead of using the comparison operator parameters they added for 3.0.
Try using
Where-Object { $_.memberof -like "ALL" }
or something like that. $_ refers to the current object in the pipeline. I couldn't find the docs for version 2.0 but I found Using the Where-Object Cmdlet for version 1.0 which was relevant for 2.0 AFAIK and should help you.

Counting Services on multiple servers

I'm still a beginner with powershell, but love to learn and research all the things it can do.
So with that, I am looking to create a script that will output to a datagridview table. I created a simple enough form that has a multi-line text box where you can enter in a list of servers and then just a simple "check" button that calls my function to actually query each server for their services and outputs that info into the datagridview below it.
That part was easy enough for me to create. My thing is, I want it to do the following":
query each server and pull the list of all services on that server
then in the datagridview, I want it to show the service name in column 1, and then the count of how many servers have that service in column 2
Hope this make since. Can't really provide any code as I don't know how to properly write it! I'm thinking there is someway to generate the full list, then do a loop and count type thing, but not sure.
I thank you for any assistance with this. Been googling and reading up like crazy. Either not correct search criteria or something just isn't clicking for me.
Update - Adding in current script I have:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$xForm = 800
$yForm = 800
$SVCForm = New-Object System.Windows.Forms.Form
$SVCForm.Text = "Automatic Services Query"
$SVCForm.Size = New-Object System.Drawing.Size($xForm,$yForm)
$SVCForm.FormBorderStyle = "FixedSingle"
$SVCForm.StartPosition = "CenterScreen"
$SVCForm.ControlBox = $true
$SVCForm.KeyPreview = $True
$SVCForm.ShowIcon = $false
$SVCForm.MinimizeBox = $True
$SVCForm.MaximizeBox = $false
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(365,720)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$x=$CancelButton.Text;$SVCForm.Close()})
$SVCForm.Controls.Add($CancelButton)
$ServerListGroup = New-Object System.Windows.Forms.GroupBox
$ServerListGroup.Location = New-Object System.Drawing.Size(5,10)
$ServerListGroup.size = New-Object System.Drawing.Size(780,300)
$ServerListGroup.text = "Enter list of servers you want to check:"
$SVCForm.Controls.Add($ServerListGroup)
$ServerList = New-object System.Windows.Forms.TextBox
$ServerList.Location = New-object System.Drawing.Size(5,25)
$ServerList.Size = New-Object System.Drawing.Size(280,270)
$ServerList.Multiline = $True
$ServerList.ScrollBars = "Vertical"
#$ServerList.add_TextChanged({ONValButton})
$ServerListGroup.Controls.Add($ServerList)
$SVCButton = New-Object System.Windows.Forms.Button
$SVCButton.Location = New-Object System.Drawing.Size(505,140)
$SVCButton.Size = New-Object System.Drawing.Size(180,22)
$SVCButton.Text = "SVC Check"
$SVCButton.Enabled = $True
$SVCButton.Add_Click({SvcCheckCount})
$ServerListGroup.Controls.Add($SVCButton)
$SvcListGroup = New-Object System.Windows.Forms.GroupBox
$SvcListGroup.Location = New-Object System.Drawing.Size(5,330)
$SvcListGroup.size = New-Object System.Drawing.Size(780,380)
$SvcListGroup.text = "Automatic Running/Not-Running Services are listed below:"
$SVCForm.Controls.Add($SvcListGroup)
#$SvcList = New-object System.Windows.Forms.TextBox
#$SvcList.Location = New-object System.Drawing.Size(5,25)
#$SvcList.Size = New-Object System.Drawing.Size(765,350)
#$SvcList.Multiline = $True
#$SvcList.ScrollBars = "Vertical"
#$SvcList.Readonly= $True
#$SvcListGroup.Controls.Add($SvcList)
$SvcGrid = New-Object System.Windows.Forms.DataGridView
$SvcGrid.Location = New-Object System.Drawing.Size(5,15)
$SvcGrid.Size = New-Object System.Drawing.Size(760,360)
$SvcGrid.ColumnHeadersBorderStyle = [System.Windows.Forms.DataGridViewHeaderBorderStyle]::Single
$SvcGrid.CellBorderStyle = [System.Windows.Forms.DataGridViewCellBorderStyle]::Single
$SvcGrid.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::DisableResizing
$SvcGrid.GridColor = [System.Drawing.Color]::Black
$SvcGrid.RowHeadersVisible = $false
$SvcGrid.AllowUserToAddRows = $false
$SvcGrid.AllowUserToResizeColumns = $False
$SvcGrid.AllowUserToResizeRows = $false
$SvcGrid.ColumnHeadersHeight = 23
$SvcGrid.SelectionMode = [System.Windows.Forms.DataGridViewSelectionMode]::FullRowSelect
$Scroll = New-Object System.Windows.Forms.VScrollBar
$Scroll.Dock = [System.Windows.Forms.DockStyle]::Right
$Scroll.width = 18
$Scroll.isAccessible = $false
$SvcGrid.Controls.Add($Scroll)
$SvcGrid.Columns.Add("Name","Service Name") > $null
$SvcGrid.Columns["Name"].Width = 200
$SvcGrid.Columns["Name"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["Name"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["Name"].ReadOnly = $False
$SvcGrid.Columns.Add("StartMode", "Start Mode") > $null
$SvcGrid.Columns["StartMode"].Width = 180
$SvcGrid.Columns["StartMode"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["StartMode"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["StartMode"].ReadOnly = $False
$SvcGrid.Columns.Add("Running","Running") > $null
$SvcGrid.Columns["Running"].Width = 180
$SvcGrid.Columns["Running"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["Running"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["Running"].ReadOnly = $true
$SvcGrid.Columns.Add("NotRunng","Not Running")>$null
$SvcGrid.Columns["NotRunng"].Width = 180
$SvcGrid.Columns["NotRunng"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["NotRunng"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["NotRunng"].ReadOnly = $true
$SvcListGroup.Controls.Add($SvcGrid)
Function SvcCheckCount
{
$SvcGrid.Rows.Clear()
$Servername = $ServerList.Text.Split("`n")|%{$_.trim()}
foreach($Server in $Servername)
{
$Server = $Server.ToUpper()
$svctest = Get-WmiObject Win32_Service | Where-Object {$_.StartMode -eq 'Auto'} | Select-Object Name, Startmode, State
Foreach ($svc in $svctest)
{
$name = $svc.Name
$startmode = $svc.StartMode
$state = $svc.State
If($state-eq"Running"){$SvcGrid.Rows.add($name,$startmode,$state)}
If($state-eq"Stopped"){$SvcGrid.Rows.add($name,$startmode,$null,$state)}
}
}
}
$SVCForm.Topmost = $True
$SVCForm.Add_Shown({$SVCForm.Activate()})
[void] $SVCForm.ShowDialog()
Since you already have the GUI portion of this functioning I am going to focus us the grouping of the data you are looking for. Going into this the one thing I am fuzzy on is why you have the if statements are for but we can work on that if need be. The following is meant to be in place of your foreach loop
$services = #()
foreach($Server in $Servername){
$Server = $Server.ToUpper()
$services += Get-WmiObject Win32_Service -ComputerName $Server -Filter 'StartMode="Auto"' |
Select-Object Name, Startmode, State |
Add-Member -MemberType NoteProperty -Value $Server -Name "Server" -PassThru
}
$services | Group-Object Name | ForEach-Object{
$SvcGrid.Rows.add($_.Name,$_.Count)
}
First thing: you were not using the $server variable in your wmi call so all the results would have been from the one machine you were running this from. Also, I moved the Where into a -Filter so that should speed up processing on remote machines.
Collect all of this information into an array (adding the computer name in case it becomes useful later). Then, using the results from that use Group-Object to get the counts you were looking for. Pipe that into another ForEach-Object to get the results in your grid ( which i have not tested.)
Side note: If you just want the info in a grid you can use Out-GridView
$services | Group-Object Name | Select name,count | Out-GridView

Unable to catch error in PowerShell script, while using quest active roles to query active directory

I have a script which returns the location of a list of computers within AD. This works fine for my current domain and if I specify "-searchroot" it works for the others.
If I query a computer not on the correct domain, with 'get-qadcomputer' it does not return any information to the console, so need a method to catch and try alternatives?
I have tried this, which just works for all computer in DC=Domain1 (my current domain):
Add-PSSnapin Quest.ActiveRoles.ADManagement
$strPath = "C:\sample.xlsx"
$objExcel = new-object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open($strPath)
$worksheetIn = $workbook.sheets.item("Asset")
$worksheetOut = $workbook.sheets.item("Asset")
$intRowMax = ($worksheetIn.UsedRange.Rows).count
$Columnnumber = 1
For($intRow = 2 ; $intRow -le $intRowMax ; $intRow++) {
Try
{
$name = $worksheetIn.cells.item($intRow,$ColumnNumber).value2
"Querying $name...$introw of $intRowMax"
$OU = Get-QADcomputer -searchroot 'OU=Workstations,DC=DOMAIN1'-LdapFilter "(CN=$Name)"`
| ft Location -HideTableHeaders
$Out = Format-list -InputObject $OU | out-string
$worksheetOut.cells.item($intRow,6) = "$Out"
}
Catch
{
$name = $worksheetIn.cells.item($intRow,$ColumnNumber)
"Querying $name...$introw of $intRowMax"
$OU = Get-QADComputer -SearchRoot 'OU=Workstations,DC=DOMAIN2' -LdapFilter (CN=$Name)"`
| ft Location -HideTableHeaders
$Out = Format-list -InputObject $OU | out-string
$worksheetOut.cells.item($intRow,6) = "$Out"
}
}
$objexcel.save()
$objExcel.workbooks.close()
$objexcel.application.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel)
Remove-Variable objExcel
You could check if $OU is equal to $null. I suspect it just isn't finding anything if you get no error text or it doesn't take you to the catch handler. Another option is to check $? right after the line calling Get-QADComputer but I suspect you will get True (command succeeded). But if you get False, then that tells you it didn't succeed.

Map network drives based on IP/subnet and AD group memberships using powershell

I am using a powershell script like the one below, it looks at AD Security group memberships and based on that it maps drives for the users. What I am trying to accomplish is that when it sees users log on from a certian IP or Subnet it maps a different set of drives in addition to the ones it grabs from AD group memberships, any insight will be appreciated.
# The section below determines what AD groups is the user member of
$strName = $env:username
function get-GroupMembership($DNName,$cGroup){
$strFilter = "(&(objectCategory=User)(samAccountName=$strName))"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.Filter = $strFilter
$objPath = $objSearcher.FindOne()
$objUser = $objPath.GetDirectoryEntry()
$DN = $objUser.distinguishedName
$strGrpFilter = "(&(objectCategory=group)(name=$cGroup))"
$objGrpSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objGrpSearcher.Filter = $strGrpFilter
$objGrpPath = $objGrpSearcher.FindOne()
If (!($objGrpPath -eq $Null)){
$objGrp = $objGrpPath.GetDirectoryEntry()
$grpDN = $objGrp.distinguishedName
$ADVal = [ADSI]"LDAP://$DN"
if ($ADVal.memberOf.Value -eq $grpDN){
$returnVal = 1
return $returnVal = 1
}else{
$returnVal = 0
return $returnVal = 0
}
}else{
$returnVal = 0
return $returnVal = 0
}
}
# The section below maps network drives based on users AD Security Group memberships
$result = get-groupMembership $strName "SecurityGrtoup1"
if ($result -eq '1') {
$(New-Object -ComObject WScript.Network).RemoveNetworkDrive("G:");
$(New-Object -ComObject WScript.Network).MapNetworkDrive("G:", "\\server1\Group");
$(New-Object -ComObject WScript.Network).RemoveNetworkDrive("P:");
$(New-Object -ComObject WScript.Network).MapNetworkDrive("P:", "\\server2\Common");
}
$result = get-groupMembership $strName "SecurityGroup3"
if ($result -eq '1') {
$(New-Object -ComObject WScript.Network).RemoveNetworkDrive("N:");
$(New-Object -ComObject WScript.Network).MapNetworkDrive("N:", "\\Server3\files");
}
This should get you the IPv4 address as a string object:
$ip = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration | Where-Object {$_.IPAddress} | Select-Object -ExpandProperty IPAddress)[0]
You could then parse that anyway you need to, you could also instead use the .IPSubnet, or .DefaultIPGateway properties if that would get you what you need.
For other available properties try:
Get-WmiObject -Class Win32_NetworkAdapterConfiguration | Where-Object {$_.IPAddress} | Get-Member