Comparing 2 CSV files with compare-object to get outdated software - powershell

I am writing a script to compare 2 csv files to get the outdated software's and put them into another csv file. I have a problem when I try to get the outdated softwares, it just shows all the softwares installed again. This is my script.
$CurrentList = Import-csv C:\Users\Administrator\CSVReports\InstalledSoftware.csv | select
ComputerName, DisplayName, DisplayVersion, Publisher
$ApprovedList = Import-csv C:\Users\Administrator\ApprovedSoftware\ApprovedSoftwareList.csv | select
DisplayName, DisplayVersion, Publisher
$ApprovedList = $ApprovedList | select ComputerName, DisplayName, DisplayVersion, Publisher
$Outdated = Compare-Object -ReferenceObject $CurrentList -DifferenceObject $ApprovedList -Property
DisplayName, DisplayVersion, Publisher -PassThru | Where Sideindicator -eq '<='
$Outdated = $Outdated | Foreach {
$Item = $_
$ApprovedVersion = $ApprovedList | Where {$_.DisplayName -eq $Item.DisplayName -and $_.Publisher -eq
$Item.Publisher } | Select -ExpandProperty DisplayVersion -First 1
[PSCustomObject]#{
ComputerName = $_.ComputerName
DisplayName = $_.DisplayName
CurrentVersion = $_.DisplayVersion
ApprovedVersion = $ApprovedVersion
Publisher = $_.Publisher
}
}
$Outdated | export-csv OutdatedSoftware.csv -NoTypeInformation
My CSV files look like this
InstalledSoftware.csv
ComputerName DisplayName DisplayVersion Publisher
Win2008R2 Google Chrome 86.0.4280.88 Google LLC
Win2008R2 VmWare Tools 10.3.5.10430147 VMWare, LLC
Win2012R2 Google Chrome 86.0.4280.88 Google LLC
Win2012R2 VmWare Tools 10.3.5.10430147 VMWare, LLC
Win2016 Google Chrome 86.0.4280.88 Google LLC
Win2016 VmWare Tools 10.3.5.10430147 VMWare, LLC
ApprovedList.csv
DisplayName DisplayVersion Publisher
Google Chrome 87.0.4280.88 Google LLC
VmWare Tools 10.3.5.10430147 VMWare, LLC
Only chrome should be in the OutDatedSoftware csv as only the chrome version is different but I get all the softwares. I also want to know which server the software is on. Appreciate the help.

I'm having weird results with Publisher. This should do the trick, however
$CurrentList = Import-csv C:\Users\Administrator\CSVReports\InstalledSoftware.csv
$ApprovedList = Import-csv C:\Users\Administrator\ApprovedSoftware\ApprovedSoftwareList.csv
$Outdated = Compare-Object -ReferenceObject $CurrentList -DifferenceObject $ApprovedList -Property DisplayName, DisplayVersion -PassThru | Where-Object Sideindicator -eq '<='
$Outdated = foreach($oldversion in $Outdated)
{
$oldversion.DisplayName
$ApprovedList | Where {$_.DisplayName -eq $oldversion.DisplayName} | ForEach-Object {
[PSCustomObject]#{
ComputerName = $oldversion.ComputerName
DisplayName = $oldversion.DisplayName
CurrentVersion = $oldversion.DisplayVersion
ApprovedVersion = $_.DisplayVersion
Publisher = $oldversion.Publisher
}
}
}
$Outdated | Export-Csv OutdatedSoftware.csv -NoTypeInformation

That new code work fine ...
Except that if your csv are tab delimited, you will need to specify it when importing the csv.
Add
`-Delimiter "`t"`
to your 2 Import-csv statements.
Import-csv C:\Users\Administrator\CSVReports\InstalledSoftware.csv | select ComputerName, DisplayName, DisplayVersion, Publisher
Import-csv C:\Users\Administrator\ApprovedSoftware\ApprovedSoftwareList.csv -Delimiter "`t" | select DisplayName, DisplayVersion, Publisher
Assuming your csv are tab-delimited properly, it should work.
If it still doesn't afterward, try the Import statement in the console alone to see what is actually imported before going further.
When testing from your example, SO did not preserve the tab delimiter so it didn't produce the proper results at first.
edit:
Based on your comment, your csv appear to be improperly formatted.
Not specifying the tab delimited for a tab delimited csv will import the csv as a 1 column object.
Incorrect -
While pretty, all the columns are linked together, meaning you actually have only one column. This can be validated by trying to access any of the column. They will all be blanks.
The only column being imported in this case is called ComputerName DisplayName DisplayVersion Publisher
Correct
Notice there is no dashes in-between each column. The csv have definitely 4 columns.

Related

How to find the MAC Address of Active Directory Computer Objects?

I am trying to get write a script where I can get all of the machine within my domains. here is what I found so far however I need to add additional information and still unable to get the correct information to get pull out. If someone can help me this will be great.
Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"' -Properties Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack,IPv4Address | Sort-Object -Property Operatingsystem | Select-Object -Property Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack, IPv4Address| ft -Wrap –Auto
I still need to be able to grab the MAC Address from all machines as well domains the machine belong to. and to make it worst I need to figure out how to export all of the data to CSV.
You will need to loop over the computers and get the MAC address individually inside the loop:
# Get-ADComputer returns these properties by default:
# DistinguishedName, GroupCategory, GroupScope, Name, ObjectClass, ObjectGUID, SamAccountName, SID
$props = 'OperatingSystem', 'OperatingSystemVersion', 'OperatingSystemServicePack', 'IPv4Address'
$result = Get-ADComputer -Filter "operatingsystem -like '*Windows server*' -and enabled -eq 'true'" -Properties $props |
Sort-Object OperatingSystem | ForEach-Object {
$mac = if ((Test-Connection -ComputerName $_.Name -Count 1 -Quiet)) {
# these alternative methods could return an array of MAC addresses
# get the MAC address using the IPv4Address of the computer
(Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='True'" -ComputerName $_.IPv4Address).MACAddress
# or use
# Invoke-Command -ComputerName $_.IPv4Address -ScriptBlock { (Get-NetAdapter | Where-Object {$_.Status -eq 'Up'}).MacAddress }
# or
# (& getmac.exe /s $_.IPv4Address /FO csv | ConvertFrom-Csv | Where-Object { $_.'Transport Name' -notmatch 'disconnected' }).'Physical Address'
}
else { $mac = 'Off-Line' }
# return the properties you want as object
$_ | Select-Object Name, OperatingSystem, OperatingSystemVersion, OperatingSystemServicePack, IPv4Address,
#{Name = 'MacAddress'; Expression = {#($mac)[0]}}
}
# output on screen
$result | Format-Table -AutoSize -Wrap
# or output to CSV file
$result | Export-Csv -Path 'X:\Wherever\ADComputers.csv' -NoTypeInformation -UseCulture
Active directory computer object doesn't contain the MAC address attribute , so you will not be able to get the info needed using active directory object only; but instead you can use the "IPv4Address" attribute of the AD computer object and query the DHCP server to find the machines MAC address and place the output data as "custompsobject" then export the result as C.V sheet.
Also if you have System center configuration manager "SCCM" you can query its database to generate a report with all needed data (Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack,IPv4Address and MAC address)

Compare-Object - How do I know which is the difference object and which is the reference object?

I have two objects and want to compare them to find users found in both.
The below appeared to work OK until I tested the output. I found that although the number of users returned was correct, the actual values weren't.
Compare-Object -ReferenceObject $PilotUsers -DifferenceObject $Leavers -IncludeEqual |
Where-Object {$_.sideindicator -eq '=='}
If I switch the ref and dif objects around I get the correct result. As I'm only interested in users in both I don't see how it matters which way around they are set. Clearly there is a need to understand how this works for future occasions. Searches online haven't shed any light on this unfortunately.
My objects are created as below. Both return a pscustomobject with one property.
$PilotUsers = Get-aduser -Filter * -Properties memberof |
Select-Object samaccountname, memberof |
Where-Object {$_.memberof -like "*Licensing_Pilot Users*"}|
Select-Object #{n='ID'; e={$_.samaccountname}}
$Leavers = Import-Csv -Path $LeaverFile.FileName |
Select-Object #{n='ID'; e={$_.payroll.substring(2,5).trimstart('0')}}
Please can someone explain or point me in the right direction? The ultimate goal is to delete users found in the leavers object from the AD group object.
First of all, your query for $pilotUsers is highly inefficient. The right syntax for the query should be:
$PilotUsers = Get-ADuser -Filter * -Properties MemberOf |
Where-Object {$_.memberof -like "*Licensing_Pilot Users*"} |
Select-Object #{n='ID'; e={$_.samaccountname}}
A more efficient way to search for users that are direct members (non recursive) of groups with name like Licensing_Pilot Users is this:
$PilotUsers = Get-ADGroup -Filter {Name -like "*Licensing_Pilot Users*"} | Get-ADGroupMember |
Where-Object {$_.ObjectClass -eq 'user'} |
Select-Object #{n='ID'; e={$_.samaccountname}}
Now for the comparison, I will assume that you're getting a list of sAMAccountNames from your CSV file. This is how you can filter both variables to find the results you want.
# To get all pilotUsers that are in leavers
$pilotUsers.ID | Where-Object {$_ -in $leavers.ID}
$pilotUsers.ID.Where({$_ -in $leavers.ID})
# To get all leavers that are in pilotUsers
$leavers.ID | Where-Object {$_ -in $pilotUsers.ID}
$leavers.ID.Where({$_ -in $pilotUsers.ID})
# To get all users that are in both arrays
#(
$pilotUsers.ID | Where-Object {$_ -in $leavers.ID}
$leavers.ID | Where-Object {$_ -in $pilotUsers.ID}
) | select -Unique
You need to specify the property name you want to use for the comparison, in this case -Property ID:
# Emulating your data sets
$ref = 1..10|Select #{Name='ID';Expression={"$_"}}
$dif = 6..15|Select #{Name='ID';Expression={"$_"}}
# This will return pure nonsense
Compare-Object -ReferenceObject $ref -DifferenceObject $dif -IncludeEqual |
Where-Object {$_.sideindicator -eq '=='}
# This will return the results you're expecting
Compare-Object -ReferenceObject $ref -DifferenceObject $dif -IncludeEqual -Property ID |
Where-Object {$_.sideindicator -eq '=='}

Need SMTP Address for Accepted Senders, ManagedBy and ModeratedBy in Distribution List

So I'm working in PowerShell to pull some data down from my exchange server.
I'm looking to get the following fields from the distribution list.
Display Name, SAM Account Name, Primary SMTP Address, Accepted Senders, Moderation Enabled, ModeratedBy, Internal Senders Only, and Managed By.
I'm using the below script to do this.
$props = #(
"DisplayName"
"SamAccountName"
"PrimarySmtpAddress"
#{Name="Accepted Senders";Expression= {(([string]($_.AcceptMessagesOnlyFromSendersOrMembers | foreach {$_.tostring().split("/")[-1]+';'})).TrimEnd(";") | foreach {$_.split(", ")[2,3,0]})}}
"ModerationEnabled"
#{Name="ModeratedBy";Expression= {([string]($_.ModeratedBy | foreach {$_.tostring().split("/")[-1]+';'})).TrimEnd(";")}}
#{Name="Internal Senders Only";E={$_.RequireSenderAuthenticationEnabled}}
#{Name="ManagedBy";E= {(([string]($_.ManagedBy | foreach {$_.tostring().split("/")[-1]+';'})).TrimEnd(";").split(", ")[2,3,0])}}
)
Get-DistributionGroup -ResultSize Unlimited | select $props | export-Csv x:\xxxxx\test6.csv -NoTypeInformation
Which works basically perfectly, except that is lists the display name of the Accepted Senders, ManagedBy and ModeratedBy instead of the smtp address.
To make it even more interesting, the email smtp format is first.last#company.com while the display names are Last, First often with additional words such as inactive and mixed in.
I have been able to format the data for managedby and accepted Senders so that the names show as first last as long as there is only one name and it doesn't have additional words in the Display Name, but I can't get it to insert a period so that I can pipe the output to a get-aduser request for the SMTP.
Anyway, let me know if you can help.
Ryan
If you want to interact with AD, you can do the following with your calculated property:
#{
n='Accepted Senders'
e={($_.acceptmessagesonlyfromsendersormembers | Foreach-Object {
(Get-AdUser -Filter "DisplayName -eq '$_'" -Property ProxyAddresses |
Select -Expand ProxyAddresses | Where-Object {$_ -cmatch '^SMTP:'}) -replace '^SMTP:'}) -join ';'}
}
I don't know if your code $_.tostring().split("/")[-1] is causing you problems or not. However, if you need that functionality, you can change to the following:
#{
n='Accepted Senders'
e={($_.acceptmessagesonlyfromsendersormembers | Foreach-Object {
(Get-AdUser -Filter "DisplayName -eq '$($_.Split('/')[-1])'" -Property ProxyAddresses |
Select -Expand ProxyAddresses | Where-Object {$_ -cmatch '^SMTP:'}) -replace '^SMTP:'}) -join ';'}
}

Manipulating CSV Formatting for HBSS Modules

I need help with manipulating data that I export to a CSV. Right now I actually learned how to merge columns together so that once it is put into a CSV the hostname shows on only 1 line and the other two columns I merge together so that each line will only have one hostname. I however need to take it a step further.
Below is my progression:
Phase 1:
This was only good because the script retrieved the data, but as you can see below Hostname1 shows up on every line for each caption and version, which would lead to several repeats of a machine name for several lines making the CSV file extremely longer than it necessarily should be.
Script:
GWMI -Class Win32_Product -Comp (GC D:\McAfeeHost.txt) |
Where {$_.Vendor -like "mcafee*" -or $_.Vendor -like "usaf*"} |
Select PSComputerName, Caption, Version |
Export-Csv -NoTypeInformation d:\Get-HBSSModules-Test.csv
Output in CSV:
PSComputerName caption version
Hostname1 McAfee Host Intrusion Prevention 8.00.0801
Hostname1 McAfee Policy Auditor Agent 6.2.0
Hostname1 McAfee DLP Endpoint 10.0.260.42
Hostname1 McAfee VirusScan Enterprise 8.8.08000
Hostname1 ACCM 3.0.4.1
Hostname1 McAfee Agent 5.00.4006
Phase 2:
I have progressed by merging the caption and version together, so that each hostname that is fetched from -Comp (GC D:\McAfeeHost.txt) shows up only once per line. While this is more of what I am looking for, it isn't the best option for formatting as shown below the in the output of the CSV.
Script:
GWMI -Class Win32_Product -Comp (GC D:\McAfeeHost.txt) |
Where {$_.Vendor -like "mcafee*" -or $_.Vendor -like "usaf*"} |
Select PSComputername, Caption, Version |
Sort-Object Caption |
Export-Csv -NoTypeInformation d:\Get-HBSSModules-Test.csv
$a = Import-Csv d:\Get-HBSSModules-Test.csv
$a | Group-Object PSComputerName |
Select-Object #{name="PSComputerName";expression={$_.Name}},
#{name="caption, version";expression={($_.Group | % { $_.Caption, $_.Version }) -join ';'}} |
Export-Csv d:\Get-HBSSModules-Test.csv -NoTypeInformation
Output to CSV:
Phase 3:
If at all possible I would like to take each caption along with its version and put them together like phase 2, but separated by columns and still only having one hostname per line as shown below:
I do not have multiple computers available, nor have I any McAffee product installed. However, here is a possible solution to your problem. You would have to adjust Vendor to PSComputerName, the -match RegEx, and add the -ComputerName or -CimSession parameter to Get-CimInstance. But the basic concept should work.
$Product = Get-CimInstance -ClassName Win32_Product | Where-Object {$_.Vendor -match '(microsoft|intel)'}
$Group = $Product | Group-Object -Property Vendor
$result = foreach ($Vendor in $Group) {
$Products = foreach ($item in $Vendor.Group) {
"$($item.Caption);$($item.Version)"
}
$Products = $Products -join ','
"$($Vendor.Name),$Products"
}
$HeaderLength = ($result | ForEach-Object {($_ -split ',').Count} | Sort-Object -Descending)[0]-1
$Header = "Vendor,"
$x = 1
$Header += do {
$x++
"Caption;Version,"
} until ($x -eq $HeaderLength)
$Header | Out-File -FilePath $env:TEMP\strange.csv
$result | Out-File -FilePath $env:TEMP\strange.csv -Append
If you open $env:TEMP\strange.csv in Excel and use the Text to columns function with , as the delimiter, you get the result es requested in your Phase 3 Output.
It's not beautiful and also it doesn*t make any sense to me, but that's what you requested for. :p
Using Group-Object and Flatten-Object:
Given:
$CSV = ConvertFrom-CSV #"
PSComputerName,caption,version
Hostname1,McAfee Host Intrusion Prevention,8.00.0801
Hostname1,McAfee Policy Auditor Agent,6.2.0
Hostname1,McAfee DLP Endpoint,10.0.260.42
Hostname1,McAfee VirusScan Enterprise,8.8.08000
Hostname1,ACCM,3.0.4.1
Hostname1,McAfee Agent,5.00.4006
Hostname2,McAfee Agent,5.00.4006
Hostname2,McAfee DLP Endpoint,10.0.260.42
Hostname2,McAfee DLP Endpoint,10.0.260.42
Hostname3,McAfee Policy Auditor Agent,6.2.0
Hostname3,McAfee DLP Endpoint,10.0.260.42
"#
Command:
$CSV | Group PSComputerName | Flatten | Format-Table
Result:
Values.1 Count Group.1.PSComputerName Group.1.caption Group.1.version Group.2.PSComputerName Group.2.caption
-------- ----- ---------------------- --------------- --------------- ---------------------- ---------------
Hostname1 6 Hostname1 McAfee Host Intrusion Prevention 8.00.0801 Hostname1 McAfee Policy A...
Hostname2 3 Hostname2 McAfee Agent 5.00.4006 Hostname2 McAfee DLP Endp...
Hostname3 2 Hostname3 McAfee Policy Auditor Agent 6.2.0 Hostname3 McAfee DLP Endp...

How to get Export-Csv to output same information as screen output?

Get-WmiObject -list | where-object {$_.name -match "win32"} | Select-Object
name,methods,properties
This displays the name, methods and properties of each Win32 class and I wanted to get this information into a CSV file.
The following however outputs doesn't output the same information.
Get-WmiObject -list | where-object {$_.name -match "win32"} | Select-Object
name,methods,properties | Export-CSV "c:\output.csv"
How can I do this?
(Updated my script as it had an error.)
You need to do some extra manual work and make sure you expand the names and join them by some delimiter:
$methods = #{n='Methods';e={ ($_.Methods | select -expand Name) -join ';'}}
$properties = #{n='Properties';e={ ($_.Properties | select -expand Name) -join ';'}}
Get-WmiObject -List |
Where-Object {$_.Name -like "Win32_*"} |
Select-Object Name,$methods,$properties |
Export-Csv .\win32.csv -NoTypeInformation
The problem here is that each WMI Object has properties that themselves are arrays and Output-CSV can't really handle that.
To fix this, you'd need to handle the arrays of arrays explicitly.
WHat specifically do you want to be output?