ConvertTo-Json on VMWare objects doesn't work - json

In powershell while converting VM objects to json ,
($json = ConvertTo-Json $vm -Compress)
i am getting "An item with the same key has already been added" exception.
PS SQLSERVER:\> C:\Users\admin\Desktop\inventory.ps1
ConvertTo-Json : An item with the same key has already been added.
At C:\Users\huradmin\Desktop\inventory.ps1:68 char:31
+ if($vm -ne $null){$json = ConvertTo-Json $vm -Compress; insertToElasticSearc ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [ConvertTo-Json], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertToJsonCommand
insertToElasticSearch : Cannot bind argument to parameter 'json' because it is null.
At C:\Users\admin\Desktop\inventory.ps1:68 char:89
+ ... icSearch -json $json -info:$true -Verbose:$true}
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [insertToElasticSearch], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,insertToElasticSearch
getVMHosts function returns a list of VM guests. Please find my code below.
function getVMHosts{
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$vcenter,
[Parameter(Mandatory=$False)]
[switch]$info=$false
)
try
{
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Importing VMWare modules" -verbose:$info
Get-Module -ListAvailable -Name "VMware.*" | Import-Module
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Connecting to Vcenter:$vcenter" -verbose:$info
[void]::$(Connect-VIServer -Server $vcenter -ErrorAction SilentlyContinue)
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting Data center servers" -verbose:$info
$DCs = Get-Datacenter
$VMs = $null
foreach($dc in $DCs)
{
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting VM servers for Data Center:$dc" -verbose:$info
$VMs=$VMs+ $(Get-Datacenter -Name $dc.Name | Get-VM -Verbose:$info| Select PowerState,Name, NumCpu,MemoryMB,GuestId,VMHost, #{N="IP Address";E={#($_.guest.IPAddress[0])}})
}
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Disconnecting from VCenter:$vcenter" -verbose:$info
Disconnect-VIServer -Server $vcenter -ErrorAction SilentlyContinue -Confirm:$false
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Returning VM Lists" -verbose:$info
return $VMs
}
catch
{
$errorMessage = "$($_.Exception.Message)`n$(($_|select -ExpandProperty invocationinfo).PositionMessage)"
Write-Warning -Message "Catched an exception in Function:$($MyInvocation.MyCommand)`n$errorMessage" -Verbose:$true
}
}
$vmHosts = getVMHosts -vcenter "vcenter"
$counter = 0
foreach($vm in $vmHosts)
{
if($vm -ne $null){$json = ConvertTo-Json $vm -Compress;insertToElasticSearch json $json -info:$true -Verbose:$true}
}

Try ConvertTo-JSON -Depth 1. Sounds like there are properties in the object that have the same name.

I don't have VCenter to verify the script, but I refactored yours a bit to make it more powershell-ly.
Notes:
CmdletBinding gives you -Verbose and other features
Any object not set to a variable is output to the pipeline by default
Return does not do what most developers would expect
function getVMHosts{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$vcenter,
)
try
{
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Importing VMWare modules"
Get-Module -ListAvailable -Name "VMware.*" | Import-Module
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Connecting to Vcenter:$vcenter"
[void]$(Connect-VIServer -Server $vcenter -ErrorAction SilentlyContinue)
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting Data center servers"
Get-Datacenter |
ForEach-Object {
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting VM servers for Data Center:$_"
Get-Datacenter -Name $_.Name |
Get-VM -Verbose:$Verbose|
Select PowerState, Name, NumCpu, MemoryMB, GuestId, VMHost, #{N="IP Address";E={#($_.guest.IPAddress[0])}}
}
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Disconnecting from VCenter:$vcenter"
[void]Disconnect-VIServer -Server $vcenter -ErrorAction SilentlyContinue -Confirm:$false
}
catch
{
$errorMessage = "$($_.Exception.Message)`n$(($_|select -ExpandProperty invocationinfo).PositionMessage)"
Write-Warning -Message "Exception caught in Function:$($MyInvocation.MyCommand)`n$errorMessage"
}
}
getVMHosts -vcenter "vcenter" |
ForEach-Object {
$json = ConvertTo-Json $_ -Compress;
insertToElasticSearch json $json -info:$true -Verbose:$true
}
}

As noam states there are objects in there causing this. Extract the base case as an example
get-vm <insertexamplevmname> | Select PowerState, Name, NumCpu, MemoryMB, GuestId, VMHost, #{N="IP Address";E={#($_.guest.IPAddress[0])}} | convertto-json -Depth 1
You will see that VMHost isn't just the name of the host it is running on but the actual host object which also has a Name property just like the VM has a Name.
So what you probably want is to extract the VMHost name as you have done for the IP addresses from the guest object.
get-vm <insertexamplevmname> | Select PowerState, Name, NumCpu, MemoryMB, GuestId, #{N="Hostname";E={#($_.VMhost.Name)}}, #{N="IP Address";E={#($_.guest.IPAddress[0])}} | convertto-json

After some fiddling, it appears to be a bug with convertto-json when the get-vm statement returns a single vm object. If more than one vm object is returned, convertto-json works. You can test yourself, replacing vm1 and vm2 with valid vm names:
get-vm -name 'vm1' | convertto-json -depth 1## fail
get-vm -name #('vm1') | convertto-json -depth 1 ## fail
get-vm -name 'vm2' | convertto-json -depth 1 ## fail
get-vm -name #('vm2') | convertto-json -depth 1 ## fail
get-vm -name #('vm1','vm2') | convertto-json -depth 1 ## success
get-vm -name #('vm2','vm1') | convertto-json -depth 1 ## success
One hackaround would be to ensure get-vm always returns two vms by including a known vm, then ignoring the known vm json element. Not recommending this solution, but may help someone in a bind.

Related

How to call a function within a foreach parallel loop

Good evening.
I'm trying to use parallelism for the first time but I don't understand how to call a function within foreach loop.
I get a series of this error: Cannot bind argument to parameter 'Path' because it is null.
This is what I've done so far:
$FolderPath = "C:\myfolder\"
function AppendLog ($client) {
$so = New-CimSessionOption -Protocol 'DCOM'
$s = New-CimSession -ComputerName $client -SessionOption $so
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -PING: OK")
$arch = Get-CimInstance –query "select * from win32_operatingsystem" -CimSession $s | select -expandproperty osarchitecture
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -ARCH:" + $arch )
$lastboot = Get-CimInstance –query "select * from win32_operatingsystem" -CimSession $s | select -expandproperty lastbootuptime
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -BOOT:" + $lastboot )
}
$funcDef = $function:AppendLog.ToString()
$clients = get-content -path (join-path $folderPath "client.txt")
$clients | ForEach-Object -parallel {
if (test-connection $_ -count 2 -Quiet)
{
$function:AppendLog = $using:funcDef
AppendLog ($_)
}
} -throttlelimit 3
Could you explain me how to pass my path?
My bad on the comment, the error you're getting is most likely coming from your function. The error is being thrown by Join-Path:
PS /> Join-Path $null 'Logs.txt'
Join-Path : Cannot bind argument to parameter 'Path' because it is null.
At line:1 char:11
+ Join-Path $null 'Logs.txt'
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Join-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand
The reason is because $FolderPath doesn't exist in the scope of your parallel loop. $folderpath should be replace with $using:folderpath inside your function.
As a side note, adding information to the same file on a parallel execution doesn't seem to be a good idea.
Last edit, I understand if this is meant to test how ForEach-Object -Parallel works but again, if the cmdlet allows remote querying / remote execution with multiple hosts at the same time, let the cmdlet handle that for you, it is more efficient.
As for the code, this is what I would use with what you already have:
$FolderPath = "C:\myfolder\"
$sessionOption = New-CimSessionOption -Protocol 'DCOM'
$clients = Get-Content -Path (Join-Path $FolderPath -ChildPath "Client.txt")
$results = $clients | ForEach-Object -Parallel {
$out = #{
Time = [datetime]::Now.ToString('[yyyy.MM.dd HH:mm:ss]')
ComputerName = $_
}
if ($ping = Test-Connection $_ -Count 2 -Quiet)
{
$session = New-CimSession -ComputerName $_ -SessionOption $using:sessionOption
$OSInfo = Get-CimInstance -CimSession $session -ClassName win32_operatingSystem
Remove-CimSession $session
}
$out.Ping = $ping
$out.Arch = $OSInfo.OSArchitecture
$out.LastBoot = $OSInfo.LastBootUpTime
[pscustomobject]$out
} -ThrottleLimit 3
$results | Export-Csv "$myFolder\LOGS.csv" -NoTypeInformation
This will output an object like this below:
Time ComputerName Ping OSArchitecture LastBoot
---- ------------ ---- -------------- --------
[2021.06.19 20:06:00] ComputerName01 True 64-bit 6/16/2021 11:47:16 AM
[2021.06.19 20:07:00] ComputerName02 False
[2021.06.19 20:08:00] ComputerName03 True 64-bit 6/13/2021 11:47:16 AM
[2021.06.19 20:09:00] ComputerName04 True 64-bit 6/14/2021 11:47:16 AM
[2021.06.19 20:10:00] ComputerName05 True 64-bit 6/15/2021 11:47:16 AM
Which can be exported nicely to a CSV instead of a text file. P.D.: sorry for the syntax highlighting :(

Logging to console and file with function passing back return code

I've decided it makes sense that functions being called by a Powershell script should
Log the same output to both a log file and to the console and
should return a status indicating success/failure.
I found a way to do this but it seems ridiculously cumbersome and backwards (illustrated below). I'm thinking this is such a basic and essential capability for any scripting language and that I must be really lost and confused to be doing something in such a backwards way. I'm pretty new to PowerShell but come from a C# background.
I ended up adding -PassThru to every Add-Content statement in the function so the log entry will be coming back in the pipeline as item of an Object[] collection. I then am passing back a final boolean item in the Object[] collection which is the status of the function.
# Main script c:\temp\test1.ps1
Function Write-FunctionOutputToConsole {
Param ([Object[]] $FunctionResults)
foreach ($item in $FunctionResults) {
if ($item -is [System.Array]) {
Write-Host $($item)
}
}
}
Function Get-FunctionReturnCode {
Param ([Object[]] $FunctionResults)
if ($FunctionResults[-1] -is [System.Boolean]) {
Return $FunctionResults[-1]
}
}
. c:\temp\test2.ps1 #pull in external function
$LogFile = "c:\temp\test.log"
$results = FunctionThatDoesStuff -LogFile $LogFile -DesiredReturnValue $true
Write-FunctionOutputToConsole -FunctionResults $results
$FunctionReturnCode = Get-FunctionReturnCode -FunctionResults $results
Add-Content -Path $LogFile -Value "$(Get-Date -Format G) Logging in Main: returnValue=$FunctionReturnCode" -PassThru
# Do some logic based on $FunctionReturnCode
External function
# c:\temp\test2.ps1
function FunctionThatDoesStuff {
Param(
[string] $LogFile,
[bool] $DesiredReturnValue
)
Add-Content -Path $LogFile -Value "-----------------------------------------" -PassThru
Add-Content -Path $LogFile -Value "$(Get-Date -Format G) returnValue=$DesiredReturnValue" -PassThru
Add-Content -Path $LogFile -Value "$(Get-Date -Format G) line 1 being logged" -PassThru
Add-Content -Path $LogFile -Value "$(Get-Date -Format G) line 2 being logged" -PassThru
return $DesiredReturnValue
}
Console Output:
PS C:\Temp> c:\temp\test1.ps1
-----------------------------------------
7/19/2018 3:26:28 PM returnValue=True
7/19/2018 3:26:28 PM line 1 being logged
7/19/2018 3:26:28 PM line 2 being logged
7/19/2018 3:26:28 PM Logging in Main: returnValue=True
Log File
PS C:\Temp> get-content c:\temp\test.log
-----------------------------------------
7/19/2018 3:29:59 PM returnValue=True
7/19/2018 3:29:59 PM line 1 being logged
7/19/2018 3:29:59 PM line 2 being logged
7/19/2018 3:29:59 PM Logging in Main: returnValue=True
As you can see this results in the identical information in the Console and logging file.
I think you're misunderstanding how PowerShell works. For one thing, the information whether or not the last command was successful is automatically stored in the automatic variable $?. In case of an error cmdlets will throw an exception that can be caught for error handling (see also). There is no need to signal success or error status with a return value. Also, PowerShell by default returns all uncaptured output from a function. The return keyword is just for control flow.
I would implement a logging function somewhat like this:
function Write-LogOutput {
[CmdletBinding()]
Param(
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
[string[]]$Message,
[Parameter(Position=1, Mandatory=$false)]
[ValidateScript({Test-Path -LiteralPath $_ -IsValid})]
[string]$LogFile = '.\default.log',
[Parameter(Mandatory=$false)]
[switch]$Quiet,
[Parameter(Mandatory=$false)]
[switch]$PassThru
)
Process {
$Message | ForEach-Object {
$msg = "[{0:yyyy-MM-dd HH:mm:ss}]`t{1}" -f (Get-Date), $_
if (-not $Quiet.IsPresent) {
$msg | Out-Host
}
$msg
} | Add-Content $LogFile
if ($PassThru.IsPresent) {
$Message
}
}
}
and then use it like this:
function FunctionThatDoesStuff {
# ...
# something that should be logged, but not returned
'foo' | Write-LogOutput -LogFile 'C:\path\to\your.log'
# something that should be logged and returned by the function
'bar' | Write-LogOutput -LogFile 'C:\path\to\your.log' -PassThru
# something that should be returned, but not logged
'baz'
# ...
}
$result = FunctionThatDoesStuff
# Output:
# -------
# [2018-07-19 23:44:07] foo
# [2018-07-19 23:44:07] bar
$result
# Output:
# -------
# bar
# baz

Powershell Google GeoLocation script

Api key here
function Get-ComputerGeoLocation ()
{
echo debug 1
# Windows Location API
$mylocation = new-object –ComObject LocationDisp.LatLongReportFactory
# Get Status
$mylocationstatus = $mylocation.status
If ($mylocationstatus -eq "4")
{
# Windows Location Status returns 4, so we're "Running"
echo debug 2
# Get Latitude and Longitude from LatlongReport property
$latitude = $mylocation.LatLongReport.Latitude
$longitude = $mylocation.LatLongReport.Longitude
if ($latitude -ne $null -or $longitude -ne $Null)
{
echo debug 3
# Retrieve Geolocation from Google Geocoding API
$webClient = New-Object System.Net.WebClient
Write-host "Retrieving geolocation for" $($latitude) $($longitude)
$url = "https://maps.googleapis.com/maps/api/geocode/xml?latlng=$latitude,$longitude'&'amp;sensor=true"
$locationinfo = $webClient.DownloadString($url)
echo debug 4
[xml][/xml]$doc = $locationinfo
# Verify the response
if ($doc.GeocodeResponse.status -eq "OK")
{
$street_address = $doc.GeocodeResponse.result | Select-Object -Property formatted_address, Type | Where-Object -Property Type -eq "street_address"
$geoobject = New-Object -TypeName PSObject
$geoobject | Add-Member -MemberType NoteProperty -Name Address -Value $street_address.formatted_address
$geoobject | Add-Member -MemberType NoteProperty -Name latitude -Value $mylocation.LatLongReport.Latitude
$geoobject | Add-Member -MemberType NoteProperty -Name longitude -Value $mylocation.LatLongReport.longitude
$geoobject | format-list
}
Else
{
Write-Warning "Request failed, unable to retrieve Geo locatiion information from Geocoding API"
}
}
Else
{
write-warning "Latitude or Longitude data missing"
}
}
Else
{
switch($mylocationstatus)
{
# All possible status property values as defined here:
# http://msdn.microsoft.com/en-us/library/windows/desktop/dd317716(v=vs.85).aspx
0 {$mylocationstatuserr = "Report not supported"}
1 {$mylocationstatuserr = "Error"}
2 {$mylocationstatuserr = "Access denied"}
3 {$mylocationstatuserr = "Initializing" }
4 {$mylocationstatuserr = "Running"}
}
If ($mylocationstatus -eq "3")
{
write-host "Windows Loction platform is $mylocationstatuserr"
sleep 5
Get-ComputerGeoLocation
}
Else
{
write-warning "Windows Loction platform: Status:$mylocationstatuserr"
}
}
} # end function
Get-ComputerGeoLocation
So I am typing in the terminal "get-computergeolocation"
The error is:
At C:\Users\xxxxx\Documents\files\powershell\computerLocation.ps1:72 char:77
+ ... write-warning "Windows Loction platform: Status:$mylocationstatuserr"
+ ~
The string is missing the terminator: ".
At C:\Users\xxxxx\Documents\files\powershell\computerLocation.ps1:4 char:1
+ {
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
I am not sure, it is giving back a syntax error, but I don't think it is a syntax error.
I sifted through the code to see if there was a syntax error and came up with nothing, at least with what the terminal was spitting out.
The code should find my latitude and longitude and give me an address (end result). This is going to be tied together in another program that determines the users home address based on IP.
====================================
UPDATED
I updated the script. I took out everything that wasn't important to keep it super simple to try to narrow down the error.
function Get-123(){
$env:GoogleGeoloc_API_Key = "key"
echo debug 1
# Windows Location API
$mylocation = new-object –ComObject LocationDisp.LatLongReportFactory
# Get Status
$mylocationstatus = $mylocation.status
If ($mylocationstatus -eq "4"){
# Windows Location Status returns 4, so we're
echo debug 2
# Get Latitude and Longitude from LatlongReport property
$latitude = $mylocation.LatLongReport.Latitude
$longitude = $mylocation.LatLongReport.Longitude
if ($latitude -ne $null -or $longitude -ne $Null){
echo debug 3
# Retrieve Geolocation from Google Geocoding API
$webClient = New-Object System.Net.WebClient
Write-host "Retrieving geolocation for" $($latitude) $($longitude)
#$u = "https://maps.googleapis.com/maps/api/geocode/xml?latlng=$latitude,$longitude"
#$r = '&'
#$l = "amp;sensor=true"
$url = “https://maps.googleapis.com/maps/api/geocode/xml?latlng=$latitude,$longitude&sensor=true”
$locationinfo = $webClient.DownloadString($url)
echo debug 4
[xml]$doc = $locationinfo
# Verify the response
if ($doc.GeocodeResponse.status -eq "OK"){
$street_address = $doc.GeocodeResponse.result | Select-Object -Property formatted_address, Type | Where-Object {$_.Property -eq "street_address"}
$geoobject = New-Object -TypeName PSObject
$geoobject | Add-Member -MemberType NoteProperty -Name Address -Value $street_address.formatted_address
$geoobject | Add-Member -MemberType NoteProperty -Name latitude -Value $mylocation.LatLongReport.Latitude
$geoobject | Add-Member -MemberType NoteProperty -Name longitude -Value $mylocation.LatLongReport.longitude
$geoobject | format-list
}
}
}
} # end function
#Get-ComputerGeoLocation
get-123
I Can't find this syntax error.... I am not even sure it is a syntax error....
At C:\Users\p617824\Documents\files\powershell\computerLocation.ps1:36 char:148
+ ... atted_address, Type | Where-Object {$_.Property -eq "street_address"}
+ ~~
The string is missing the terminator: ".
At C:\Users\p617824\Documents\files\powershell\computerLocation.ps1:4 char:1
+ {
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

try-catch bypassing a step

I have a script that tests connection to a list of servers, and if contactable, gets the status of a service, and puts the results into three variables, $Computer, $Ping (True/False), and $Service (Running or Stopped).
The output is in a hashtable but I can only get to show the servers that ARE contactable, and not the ones that cannot be contactable.
I have placed a try/catch in the $Ping block, as well as -ErrorAction Stop, so that it doesn't attempt to run the $Service script, and instead go to the next $Computer in the array. I think I am trying to do two things at once that are conflicting each other:
add the variables to the #Splat and
don't process any further.
There are actually many more remote registry queries in my script, which will be irrelevant if the $Computer cannot be contactable, but I have shortened it for this post.
Function Get-Ping {
$Servers = (gc "c:\temp\test.txt")
foreach ($Computer in $Servers) {
Write-Host
Write-Host "---------------------------------"
Write-Host "QUERYING $Computer"
Write-Host
Write-Host "Performing ping test..."
try {
$Ping = Test-Connection $Computer -Count 1 -ErrorAction Stop
} catch {
Write-Warning "Cannot Ping $Computer"
Write-Host "Trying next computer..."
Write-Host
continue
}
if ($Ping) {$Ping="$True"}
Write-Host $Computer "can be pinged"
$svcRRStopped = $false
if ($Computer -ne $env:COMPUTERNAME) {
Write-Host "Check RemoteRegistry status..."
}
$svcRR = Get-Service -ComputerName $Computer -Include RemoteRegistry
$SelectSplat = #{
Property = (
'Computer',
'Ping',
'Service'
)}
New-Object -TypeName PSObject -Property #{
Computer=$Computer
Ping=$Ping
Service=$svcRR.status
} | Select-Object #SelectSplat
}
}
$results = Get-Ping
$tableFragment = $results | Select 'Computer','Ping','Service'
$tableFragment
Don't make things more complicated than they need to be.
function Get-Ping {
Get-Content 'C:\temp\test.txt' | ForEach-Object {
$isAvailable = [bool](Test-Connection $_ -Count 1 -EA SilentlyContinue)
if ($isAvailable) {
$rreg = Get-Service -Computer $_ -Name RemoteRegistry |
Select-Object -Expand Status
} else {
$rreg = 'n/a'
}
New-Object -Type PSObject -Property #{
Computer = $_
Ping = $isAvailable
Service = $rreg
}
}
}
Get-Ping
You can simply use the -Quiet Parameter:
Test-Connection $_ -Count 1 -Quiet

how can I query to multiple domain's computer account in powershell

I have bulk computer list in my CSV file with header of servers.
All these servers are different domains under single forest.
I need to get all these server attribute details like name and operating system, status.
I have created below script but that's not working..
Any help would be appreciated.
Import-Module ActiveDirectory
# For each domain in the forest
$domains = (Get-ADForest).Domains
$servers = Import-Csv "D:\temp\computer.csv" | % {$_.server}
foreach ($server in $servers)
{
foreach ($domain in $domains)
{
Get-ADComputer $server -Server $domain -Properties operatingsystem | select name,operatingsystem
}
}
#
HI
I have added my script like below:
#
Import-Module ActiveDirectory
# For each domain in the forest
$domains = (Get-ADForest).Domains
$servers = Import-Csv "D:\temp\computers.csv" | % {$_.server}
$DomainController = "DC2:3268" # 3268 is the commen port of global catalogue
$SearchBase = ((Get-ADDomain (Get-ADForest).RootDomain).DistinguishedName)
foreach ($server in $servers)
{
foreach ($domain in $domains)
{
Get-ADComputer $server -Server $DomainController -SearchBase $SearchBase -Properties operatingsystem | select name,operatingsystem
}
}
#
Getting below error now and also I have specified only samaccountname of computer not FQDS this time..
#### Error
Get-ADComputer : A positional parameter cannot be found that accepts argument 'DPS002'.
At D:\temp\search_computer.ps1:34 char:5
+ Get-ADComputer $server -Server $DomainController -SearchBase $SearchBase -Pr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.GetADComputer
You have to run your request against the global catalogue to find AD objects in the whole AD forest.
You need a server witch is supporting global catalogue. Choose one which is next to you.
Import-Module ActiveDirectory
#((Get-ADForest).GlobalCatalogs) | Sort-Object
Your script, a little bit modified
Import-Module ActiveDirectory
$DomainController = "ServerFromStep1:3268" # 3268 is the commen port of global catalogue
$SearchBase = ((Get-ADDomain (Get-ADForest).RootDomain).DistinguishedName)
foreach ($server in $servers)
{
Get-ADComputer $server -Server $DomainController -SearchBase $SearchBase -Properties operatingsystem | select name,operatingsystem
}
The same as 2 but able to processed FQDNs from server list.
foreach ($serverFQDN in $servers)
{
$Local:ServerName = (($serverFQDN -replace "\..*$", "").Trim())
if ($ServerName) {
Get-ADComputer $ServerName -Server $DomainController -SearchBase $SearchBase -Properties operatingsystem | select name,operatingsystem
}
}