Powershell: Exporting AD MachineName/UserName/IPv4Address to a single *.csv - powershell

I have an OU that is full of default-named machines. The problem is these machines have already been sent out through 50+ sites and I don't know who has what. The below code, has done the job but I have to merge the two CSV's which isn't all that complicated but it's a step that I don't think I have to have.
Here is my current code (working with two CSV's):
###Creates temp-function to get the current user logged into machine###
function Get-LoggedOnUser {
[CmdletBinding()]
param(
[Parameter()]
[ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1 })]
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($comp in $ComputerName) {
$output = #{ 'ComputerName' = $comp }
$output.UserName = (Get-WmiObject -Class Win32_ComputerSystem -ComputerName $comp).UserName
[PSCustomObject]$output
}
}
###Change the -SearchBase parameters to the appropriate container.#######
$computer = Get-ADComputer -Filter * -SearchBase "OU=IMAGE,OU=WORKSTATIONS,DC=AD,DC=XXX,DC=US" |
Sort-Object Name
$allinfo = #()
foreach ($machine in $computer.Name) {
$Array = "" | Select-Object Machine
$array.Machine = $machine
Get-LoggedOnUser -ComputerName $machine |
Export-Csv "C:\Users\XXX\Desktop\loggedOnUsers.csv" -NoTypeInformation -Append
Get-LoggedOnUser -ComputerName $machine |
Test-Connection -Count 1 |
Select-Object #{ n = "Machine"; e = { $_.Address } }, Ipv4Address |
Export-Csv "C:\Users\XXXX\Desktop\ips.csv" -NoTypeInformation -Append
}
I have edited the code with XXX in some places, I'm not questioning those areas. I can't seem to get the end of the code to merge into one CSV.
Any help would be most appreciated.

I would suggest adding IP address to your function object like this:
function Get-LoggedOnUser {
[CmdletBinding()]
param(
[Parameter()]
[ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1})]
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($comp in $ComputerName) {
$output = #{ 'ComputerName' = $comp }
$output.UserName = (Get-WmiObject -Class Win32_ComputerSystem -ComputerName $comp).UserName
$output.Ipv4Address = (Test-Connection -ComputerName $machine -Count 1 | Select-Object -ExpandProperty Ipv4Address)
[PSCustomObject]$output
}
}
###Change the -SearchBase parameters to the appropriate container.#######
$computer = Get-ADComputer -Filter * -SearchBase "OU=IMAGE,OU=WORKSTATIONS,DC=AD,DC=XXX,DC=US" |
Sort-Object Name
$allinfo = #()
foreach ($machine in $computer.Name) {
$Array = "" | Select-Object Machine
$array.Machine = $machine
Get-LoggedOnUser -ComputerName $machine | Export-Csv "C:\Users\XXX\Desktop\\AllInOne.csv" -NoTypeInformation -Append
}

Related

find SQL Version on remote server

the code shows the InstalledInstances of server but i also need the version of the sql server. Could someone help me here how to do this?
Param(
[Parameter(Mandatory=$true)]
$ComputerName
)
if ((Get-WMIObject -Class MSCluster_ResourceGroup -ComputerName $ComputerName -Namespace root\mscluster -ErrorAction SilentlyContinue) -ne $null)
{
Import-Module FailoverClusters
get-clusterresource -Cluster $ComputerName -ErrorAction SilentlyContinue|
where-object {$_.ResourceType -like “SQL Server”} |
get-clusterparameter VirtualServerName,InstanceName | group-object ClusterObject |
select-object #{Name = “SQLInstance”;Expression = {[string]::join(“\”,($_.Group | select-object -expandproperty Value))}}
}
else {
$SQLInstances = Invoke-Command -ComputerName $ComputerName {
(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances
}
foreach ($sql in $SQLInstances) {
[PSCustomObject]#{
ServerName = $sql.PSComputerName
InstanceName = $sql
}
}
}
thanks!

How to show remote computers mapped drive path?

I got this to work locally however on a remote system it doesn't show the path but only the drive letter. My goal is to get it to show the drive path of the remote host.
Also sometimes it doesn't show all the drive that are mapped to the remote computer and I don't know why.
I have tried changing Win32_LogicalDisk to MappedLogicalDisk but it just results to no information.
$DISK = Get-WmiObject -computer $compname Win32_LogicalDisk
foreach ($device in $DISK){
Write-Host "Drive: " $device.name
Write-Host "Path: " $device.ProviderName
""
}
Pause
CheckHost
Try one of these examples:
This one...
$ComputerName = "ServerName"
gwmi win32_mappedlogicaldisk -ComputerName $ComputerName |
select SystemName, Name, ProviderName, SessionID |
foreach {
$disk = $_
$user = gwmi Win32_LoggedOnUser -ComputerName $ComputerName |
where { ($_.Dependent.split("=")[-1] -replace '"') -eq $disk.SessionID} |
foreach {$_.Antecedent.split("=")[-1] -replace '"'}
$disk | select Name, ProviderName, #{n = "MappedTo"; e = {$user} }
}
Or this one
function Get-MappedDrives($ComputerName)
{
$output = #()
if(Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
{
$Hive = [long]$HIVE_HKU = 2147483651
$sessions = Get-WmiObject -ComputerName $ComputerName -Class win32_process |
?{$_.name -eq "explorer.exe"}
if($sessions)
{
foreach($explorer in $sessions)
{
$sid = ($explorer.GetOwnerSid()).sid
$owner = $explorer.GetOwner()
$RegProv = get-WmiObject -List -Namespace "root\default" -ComputerName $ComputerName |
Where-Object {$_.Name -eq "StdRegProv"}
$DriveList = $RegProv.EnumKey($Hive, "$($sid)\Network")
if($DriveList.sNames.count -gt 0)
{
foreach($drive in $DriveList.sNames)
{
$output += "$($drive)`t$(($RegProv.GetStringValue($Hive, "$($sid)\Network\$($drive)",
"RemotePath")).sValue)`t$($owner.Domain)`t$($owner.user)`t$($ComputerName)"
}
}
else{write-debug "No mapped drives on $($ComputerName)"}
}
}
else{write-debug "explorer.exe not running on $($ComputerName)"}
}
else{write-debug "Can't connect to $($ComputerName)"}
return $output
}
<#
#Enable if you want to see the write-debug messages
$DebugPreference = "Continue"
$list = "Server01", "Server02"
$report = $(foreach($ComputerName in $list){Get-MappedDrives $ComputerName}) |
ConvertFrom-Csv -Delimiter `t -Header Drive, Path, Domain, User, Computer
#>

PowerShell Set-ADComputer description based on the input

I have a code which runs trough Active Directory computers objects to collect information. Part of that information is then updated on Active directory description field.
My problem is that when I get the Exception.Message the AD object of a computer is still updated with the last found computer information.
I would like to find out how can I:
Update AD description when there is Exception.Message
Update Ad description with populated system info
Attached is the script I'm using but can't figure out where to put the output for the two Set-ADComputer statements
# Getting computers from Active Directory
$Computers = Get-ADComputer -Filter {Enabled -eq $true} | select -expand name
Foreach($computer in $computers)
# Checking if the computer is Online
{
if(!(Test-Connection -ComputerName $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{write-host "Cannot reach $Computer is offline" -ForegroundColor red}
else {
$Output = #()
Try{
$xx = Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction Stop
$in = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer -ErrorAction Stop
$mc = Get-WmiObject -class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='True'" -ComputerName $Computer -ErrorAction Stop
$sr = Get-WmiObject win32_bios -ComputerName $Computer -ErrorAction Stop
$Xr = Get-WmiObject –class Win32_processor -ComputerName $Computer -ErrorAction Stop
$ld = Get-ADComputer $Computer -properties Name,Lastlogondate,ipv4Address,enabled,description,DistinguishedName -ErrorAction Stop
$r = "{0} GB" -f ((Get-WmiObject Win32_PhysicalMemory -ComputerName $Computer -ErrorAction Stop | Measure-Object Capacity -Sum).Sum / 1GB)
$x = Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction Stop | select #{Name = "Type";Expression = {if (($_.pcsystemtype -eq '2') )
{'Laptop'} Else {'Desktop Or Other'}}},Manufacturer,#{Name = "Model";Expression = {if (($_.model -eq "$null") ) {'Virtual'} Else {$_.model}}},username
$t= New-Object PSObject -Property #{
SerialNumber = $sr.serialnumber -replace "-.*"
Computername = $ld.name
IPaddress = $ld.ipv4Address
MACaddress = $mc.MACAddress
Enabled = $ld.Enabled
Description = $ld.description
OU = $ld.DistinguishedName.split(',')[1].split('=')[1]
DC = $xx.domain
Type = $x.type
Manufacturer = $x.Manufacturer
Model = $x.Model
RAM = $R
ProcessorName = ($xr.name | Out-String).Trim()
NumberOfCores = ($xr.NumberOfCores | Out-String).Trim()
NumberOfLogicalProcessors = ($xr.NumberOfLogicalProcessors | Out-String).Trim()
Addresswidth = ($xr.Addresswidth | Out-String).Trim()
Operatingsystem = $in.caption
InstallDate = ([WMI] '').ConvertToDateTime($in.installDate)
LastLogonDate = $ld.lastlogondate
LoggedinUser = $x.username
}
$Output += $t
}
Catch [Exception]
{
$ErrorMessage = $_.Exception.Message
Add-Content -value "$Computer, $ErrorMessage, skipping to next" $txt
Set-ADComputer $Computer -Description $ErrorMessage
}
}
# Output file location to be chnaged as needed
$file="C:\scripts\reports\AD-Inentory_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$txt="c:\scripts\reports\AD-Inentory-error_$((Get-Date).ToString('MM-dd-yyyy')).txt"
$desc="$($mc.MACAddress) ( $($sr.serialnumber -replace "-.*") ) $($x.Model) | $((Get-Date).ToString('MM-dd-yyyy'))"
# Properties to be included in the output file
$Output | select Computername,Enabled,Description,IPaddress,MACaddress,OU,DC,Type,SerialNumber,Manufacturer,Model,RAM,ProcessorName,NumberOfCores,NumberOfLogicalProcessors,Addresswidth,Operatingsystem,InstallDate,LoggedinUser,LastLogonDate | export-csv -Append $file -NoTypeInformation
Set-ADComputer $Computer -Description $desc -verbose
}
It looks like the reason why it had the problem behaviour is it was catching the error, and setting the description as you wanted with the error.
However it would then continue on to evaluate the code outside of the catch and the else block, since it failed on the current computer the variables used to build the description variable still contained data from the previous computer that was successful and then update the failed computer again.
You could fix this by using the continue statement at the bottom of the catch block to skip the rest of the code for that iteration and move to the next item in the loop.
That solution would look like this:
Catch [Exception]
{
$ErrorMessage = $_.Exception.Message
Add-Content -value "$Computer, $ErrorMessage, skipping to next" $txt
Set-ADComputer $Computer -Description $ErrorMessage
## add continue statement here
continue
}
continue statement documentation here and examples here.
The other option is you restructure your code to make sure this cannot happen, like below.
I think this will fix your issue and do what you are trying to achieve. I put comments in the code around the changes I made (denoted by the ##) feel free to ask questions.
I would recommend you use more descriptive variable names.
## No Need to define these in the foreach loop
# Output file location to be chnaged as needed
$file = "C:\scripts\reports\AD-Inentory_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$txt = "c:\scripts\reports\AD-Inentory-error_$((Get-Date).ToString('MM-dd-yyyy')).txt"
# Getting computers from Active Directory
## Update to use pipeline
Get-ADComputer -Filter {Enabled -eq $true} | Foreach-Object {
$computer = $_.Name
if(!(Test-Connection -ComputerName $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
write-host "Cannot reach $Computer is offline" -ForegroundColor red
}
else
{
Try
{
## No Longer Need this because we are uisng the pipe line
#$Output = #()
$xx = Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction Stop
$in = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer -ErrorAction Stop
$mc = Get-WmiObject -class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='True'" -ComputerName $Computer -ErrorAction Stop
$sr = Get-WmiObject win32_bios -ComputerName $Computer -ErrorAction Stop
$Xr = Get-WmiObject –class Win32_processor -ComputerName $Computer -ErrorAction Stop
$ld = Get-ADComputer $Computer -properties Name, Lastlogondate, ipv4Address, enabled, description, DistinguishedName -ErrorAction Stop
$r = "{0} GB" -f ((Get-WmiObject Win32_PhysicalMemory -ComputerName $Computer -ErrorAction Stop | Measure-Object Capacity -Sum).Sum / 1GB)
$x = Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction Stop | select #{Name = "Type"; Expression = {if (($_.pcsystemtype -eq '2') )
{'Laptop'} Else {'Desktop Or Other'}}
}, Manufacturer, #{Name = "Model"; Expression = {if (($_.model -eq "$null") ) {'Virtual'} Else {$_.model}}}, username
## Output on creation
New-Object PSObject -Property #{
SerialNumber = $sr.serialnumber -replace "-.*"
Computername = $ld.name
IPaddress = $ld.ipv4Address
MACaddress = $mc.MACAddress
Enabled = $ld.Enabled
Description = $ld.description
OU = $ld.DistinguishedName.split(',')[1].split('=')[1]
DC = $xx.domain
Type = $x.type
Manufacturer = $x.Manufacturer
Model = $x.Model
RAM = $R
ProcessorName = ($xr.name | Out-String).Trim()
NumberOfCores = ($xr.NumberOfCores | Out-String).Trim()
NumberOfLogicalProcessors = ($xr.NumberOfLogicalProcessors | Out-String).Trim()
Addresswidth = ($xr.Addresswidth | Out-String).Trim()
Operatingsystem = $in.caption
InstallDate = ([WMI] '').ConvertToDateTime($in.installDate)
LastLogonDate = $ld.lastlogondate
LoggedinUser = $x.username
}
## Only do this kind of update if it hasnt failed yet
$desc = "$($mc.MACAddress) ( $($sr.serialnumber -replace "-.*") ) $($x.Model) | $((Get-Date).ToString('MM-dd-yyyy'))"
# Properties to be included in the output file
Set-ADComputer $Computer -Description $desc -verbose
## No longer need this
# $t
}
Catch [Exception]
{
$ErrorMessage = $_.Exception.Message
Add-Content -value "$Computer, $ErrorMessage, skipping to next" $txt
Set-ADComputer $Computer -Description $ErrorMessage
continue
}
}
} | select Computername, Enabled, Description, IPaddress, MACaddress, OU, DC, Type, SerialNumber, Manufacturer, Model, RAM, ProcessorName, NumberOfCores, NumberOfLogicalProcessors, Addresswidth, Operatingsystem, InstallDate, LoggedinUser, LastLogonDate | export-csv -Append $file -NoTypeInformation
After looking at the suggestion to include continue statement I was able to achieve the solution to my problem with the following final script:
# Output file location to be changed as needed
$file="C:\scripts\reports\AD-Inentory_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$txt="c:\scripts\reports\AD-Inentory-error_$((Get-Date).ToString('MM-dd-yyyy')).txt"
# Getting computers from Active Directory
$Computers = Get-ADComputer -Filter {Enabled -eq $true} | select -expand name
Foreach($computer in $computers){
if(!(Test-Connection -ComputerName $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
write-host "Cannot reach $Computer is offline" -ForegroundColor red
}
else
{
$Output = #()
Try
{
$xx = Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction Stop
$in = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer -ErrorAction Stop
$mc = Get-WmiObject -class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='True'" -ComputerName $Computer -ErrorAction Stop
$sr = Get-WmiObject win32_bios -ComputerName $Computer -ErrorAction Stop
$Xr = Get-WmiObject –class Win32_processor -ComputerName $Computer -ErrorAction Stop
$ld = Get-ADComputer $Computer -properties Name,Lastlogondate,ipv4Address,enabled,description,DistinguishedName -ErrorAction Stop
$r = "{0} GB" -f ((Get-WmiObject Win32_PhysicalMemory -ComputerName $Computer -ErrorAction Stop | Measure-Object Capacity -Sum).Sum / 1GB)
$x = Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction Stop | select #{Name = "Type";Expression = {if (($_.pcsystemtype -eq '2') )
{'Laptop'} Else {'Desktop Or Other'}}
},Manufacturer,#{Name = "Model";Expression = {if (($_.model -eq "$null") ) {'Virtual'} Else {$_.model}}},username
## Output on creation
$t= New-Object PSObject -Property #{
SerialNumber = $sr.serialnumber -replace "-.*"
Computername = $ld.name
IPaddress = $ld.ipv4Address
MACaddress = $mc.MACAddress
Enabled = $ld.Enabled
Description = $ld.description
OU = $ld.DistinguishedName.split(',')[1].split('=')[1]
DC = $xx.domain
Type = $x.type
Manufacturer = $x.Manufacturer
Model = $x.Model
RAM = $R
ProcessorName = ($xr.name | Out-String).Trim()
NumberOfCores = ($xr.NumberOfCores | Out-String).Trim()
NumberOfLogicalProcessors = ($xr.NumberOfLogicalProcessors | Out-String).Trim()
Addresswidth = ($xr.Addresswidth | Out-String).Trim()
Operatingsystem = $in.caption
InstallDate = ([WMI] '').ConvertToDateTime($in.installDate)
LastLogonDate = $ld.lastlogondate
LoggedinUser = $x.username
}
# Only do this kind of update if it hasn't failed yet
$Output += $t
$desc="$($mc.MACAddress) ( $($sr.serialnumber -replace "-.*") ) $($x.Model) | $((Get-Date).ToString('MM-dd-yyyy'))"
Set-ADComputer $Computer -Description $desc -verbose
$Output | select Computername,Enabled,Description,IPaddress,MACaddress,OU,DC,Type,SerialNumber,Manufacturer,Model,RAM,ProcessorName,NumberOfCores,NumberOfLogicalProcessors,Addresswidth,Operatingsystem,InstallDate,LoggedinUser,LastLogonDate | export-csv -Append $file -NoTypeInformation
}
Catch [Exception]
{
# Only do this kind of update if it has failed
$ErrorMessage = $_.Exception.Message
Add-Content -value "$Computer, $ErrorMessage, skipping to next" $txt
Set-ADComputer $Computer -Description $ErrorMessage
continue
}
}
}

Options to use different param

I'm looking for a way to to have a choice of a list or a single computername in a foreach loop.
If the user enters in a single computername I want the script to execute for that one computername
but if that user wants to use a path to a list of computers how could I replace $computername with the path that user wants?
function Get-OSInfo {
[CmdletBinding()]
param (
#[Parameter(ValueFromPipeline=$True,
# ValueFromPipelineByPropertyName=$True)]
[string]$computername,
[string]$errorlog = 'c:\errors.txt',
[switch]$logerrors
)
PROCESS {
foreach ($computer in $computername) {
Try {
$os = Get-WmiObject -EA Stop –Class Win32_OperatingSystem –ComputerName $computer
$cs = Get-WmiObject -EA Stop –Class Win32_ComputerSystem –ComputerName $computer
$bios = Get-WmiObject -EA Stop –Class Win32_BIOS –ComputerName $computer
$cpu = Get-WmiObject -EA Stop -class Win32_processor -ComputerName $computer
$props = #{'ComputerName'=$computer;
'OSVersion'=$os.version;
'SPVersion'=$os.servicepackmajorversion;
'OSBuild'=$os.buildnumber;
'OSArchitecture'=$os.osarchitecture;
'Manufacturer'=$cs.manufacturer;
'Model'=$cs.model;
'BIOSSerial'=$bios.serialnumber
'CPU Count'=$CPU.Count
'Memory'= [Math]::round(($cs.TotalPhysicalMemory/1gb),2)
'CPU Speed'= $CPU.MaxClockSpeed[0]}
$obj = New-Object -TypeName PSOBject -Property $props
$obj.PSObject.TypeNames.Insert(0,'Get-OS.OSInfo')
#Write-Output $obj
$obj | Export-Csv c:\test4.csv -Append
} Catch {
if ($logerrors) {
$computer | Out-File $errorlog -append
}
Write-Warning "$computer failed"
}
}
}
}
Change the type of the $ComputerName parameter to a string array instead of just a single string:
param(
[string[]]$ComputerName,
[string]$errorlog = 'c:\errors.txt',
[switch]$logerrors
)
Notice the [] after the type name, this denotes an array of strings, rather than a single string.
Now you can do:
PS C:\> $computers = Get-Content C:\computers.txt
PS C:\> Get-OSInfo -ComputerName $computers
If you'd like to be able to specify a path to a file containing the target computers as the argument to the function, you can use multiple parameter sets:
[CmdletBinding(DefaultParameterSetName='ByName')]
param(
[Parameter(ParameterSetName='ByName',ValueFromPipeline)]
[string[]]$ComputerName,
[Parameter(ParameterSetName='ByFile')]
[string]$InputFile
)
begin {
if($PSCmdlet.ParameterSetName -eq 'ByFile'){
try{
$ComputerName = Get-Content -LiteralPath $InputFile
}
catch{
throw
return
}
}
}
process {
foreach($Computer in $ComputerName){
# Work with $Computer here...
}
}

Retrieve list of PCs that are not running a specific process

How do I get a list of PCs that don't have a process running with this script that I wrote?
<#
Searches AD for all computers that can ping and checks to see if a process
is running
#>
Import-Module active*
$PingTest = $null
$Clist = #()
Get-ADComputer -Filter * -Properties * | ? {$_.operatingsystem -like "*windows 7*"} |
ForEach-Object {
# test to see if the computer is on the network
$PingTest = Test-Connection -ComputerName $_.name -Count 1 -BufferSize 16 -Quiet
# If test is $true adds each computer to the array $Clist
If ($PingTest) {$Clist += $_.name}
Else {}
}#ForEach
#check for process running on each computer in the array $Clist
Invoke-Command -ComputerName $Clist -ScriptBlock {Get-Process -Name mcshield}
Use Get-Process inside an If statement. If a process is returned it will evaluate to true. You could then export the list out as a spreadsheet using Export-Csv
$Computers = Get-ADComputer -Filter "OperatingSystem -like '*Windows 7*'"
$ProcessRunning = $Computers |
ForEach-Object {
If ( Test-Connection -ComputerName $_.name -Count 1 -BufferSize 16 -Quiet ) {
If (Get-Process -ComputerName $_.name -Name mcshield -ErrorAction SilentlyContinue) {
[pscustomobject]#{
'ComputerName' = $_.name
'Process Running' = $True
}
} Else {
[pscustomobject]#{
'ComputerName' = $_.name
'Process Running' = $False
}
}
}
}
$ProcessRunning | Export-Csv C:\example\path.csv -NoTypeInformation