Export installed software to csv from multiple remote systems - powershell

My script does (somewhat) what it's supposed to do if I need the information from the local PC. I have it reaching to a txt file for a list of computers. The script will read the list and show "Processing:..." for the list of computers but the export only shows the local system. Even if I remove my local PC from the txt file, the export to csv still only shows the local system. It does create the correct columns and populates accordingly. I just need it to pull the data from remote systems as well. Since the script will be used by multiple people, the variables allow the txt file and output file to be on their systems.
Function Export-InstalledSoftware {
Process {
if (Test-Connection -ComputerName $_ -count 1 -quiet)
{
echo "Processing: $env:COMPUTERNAME"
Get-ItemProperty -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*,HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* `
| Select-object #{n='ComputerName';e={$env:COMPUTERNAME}},#{n='DisplayName';e={$_.displayName}},#{n='Publisher';e={$_.Publisher}},#{n='DisplayVersion';e={$_.DisplayVersion}} `
| Sort displayName `
| Export-CSV C:\Users\$(get-content env:username)\Desktop\"SoftwareQuery_"$(get-date -f yyyy-MM-dd)".csv" -NoTypeInformation -Encoding ASCII
}
}
}
Get-Content "C:\Users\$(get-content env:username)\Desktop\systems.txt" -readcount 1 | Export-InstalledSoftware

Related

Export-CSV is not populating separate CSV files based on source

Good morning,
Hopefully this will be a quick and easy one to answer.
I am trying to run a PS script and have it export to csv based on a list of IP addresses from a text file. At the moment, it will run but only produce one csv.
Code Revision 1
$computers = get-content "pathway.txt"
$source = "\\$computer\c$"
foreach ($computer in $computers) {
Get-ChildItem -Path "\\$Source\c$" -Recurse -Force -ErrorAction SilentlyContinue |
Select-Object Name,Extension,FullName,CreationTime,LastAccessTime,LastWriteTime,Length |
Export-CSV -Path "C:\path\$computer.csv" -NoTypeInformation
}
Edit
The script is now creating the individual server files as needed and I did change the source .txt file to list the servers by HostName rather than IP. The issue now is that no data is populating in the .csv files. It will create them but nothing populates. I have tried different source file paths to see if maybe its due to folder permissions or just empty but nothing seems to populate in the files.
The $computer file lists a number of server IP addresses so the script should run against each IP and then write out to a separate csv file with the results, naming the csv file the individual IP address accordingly.
Does anyone see any errors in the script that I provided, that would prevent it from writing out to a separate csv with each run? I feel like it has something to do with the foreach loop but I cannot seem to isolate where I am going wrong.
Also, I cannot use any third-party software as this is a closed network with very strict FW rules so I am left with powershell (which is okay). And yes this will be a very long run for each of the servers but I am okay with that.
Edit
I did forget to mention that when I run the script, I get an error indicating that the export-csv path is too long which doesn't make any sense unless it is trying to write all of the IP addresses to a single name.
"Export-CSV : The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
At line:14 char:1
TIA
Running the script against C: Drive of each computer is strongly not advisable that too with Recurse option. But for your understanding, this is how you should pass the values to the variables. I haven't tested this code.
$computer = get-content "pathway.txt"
foreach ($Source in $computer) {
Get-ChildItem -Path "\\$Source\c$" -Recurse -Force -ErrorAction SilentlyContinue |
Select-Object Name,Extension,FullName,CreationTime,LastAccessTime,LastWriteTime,Length | Export-Csv -Path "C:\Path\$source.csv" -NoTypeInformation
}
$computer will hold the whole content and foreach will loop the content and $source will get one IP at a time. I also suggest instead of IP's you can have hostname so that your output file have servername.csv for each server.
In hopes that this helps someone else. I have finally got the script to run and create the individual .csv files for each server hostname.
$servers = Get-Content "path"
Foreach ($server in $servers)
{
Get-ChildItem -Path "\\$server\c$" -Recurse -Force -ErrorAction SilentlyContinue |
Select-Object Name,Extension,FullName,CreationTime,LastAccessTime,LastWriteTime,Length |
Export-CSV -Path "path\$server.csv" -NoTypeInformation
}

Pulling Win10 activation codes from remote computers

I'm brand new to PS scripting, so bear with me :)
I'm trying to create a PS script that will write the Win10 activation code to a file then copy that file to a central repo to then manually activate.
I'm creating a PS script and trying to run
cscript.exe c:\windows\system32\slmgr.vbs -dti >
$SourceDir\$env:computername.txt
$SourceDir = \\computer01\c$\temp
I need to run it from one computer, remotely connecting to every computer on the network, creating the computername.txt file then copying that file back to a central repository for all the files.
What I have so far:
$s1=New-PSSession -ComputerName computer01 -Credential $AdminCred
Test-Connection -ComputerName computer01
$id='\\computer01\windows\system32'
$SourceDir='\\computer01\c$\temp'
md $SourceDir
$GetActID=cscript.exe $id\slmgr.vbs -dti >
$SourceDir\$env:computername.txt
Invoke-Command -Session $s1 -ScriptBlock { $Using:GetActID }
Then I call a batch file that copies the computername.txt file from the computer01 over to a repository where they are going to sit.
I FINALLY got it working correctly except for the name of the file isn't naming it to the computer01, it's naming it with the hostname of the computer I'm running it from, therefore the filenames are identical. I had the naming piece working, but I had to change the way I was remoting into the computer and now it's not naming correctly.
Any idea on how I could get it to name the file to be related to the remote computer?
**I'm still working on the whole piece of the puzzle where it goes back to an excel sheet pulled from AD and pulls the host names from that sheet to connect to each machine, I believe I'll be adding a ForEach syntax in there somehow for that.
Although not sure how you are getting the list of "every computer on the network", chances are you are doing this using
# get a list of all AD computers (their names only)
$computers = (Get-ADComputer -Filter *).Name
Then I think you don't need to have every computer save the file on its own disk and later copy these files to a central share.
Instead, just capture the info in a variable and after the loop write the file to the central share as structured CSV file combining all computernames and install id's so you can open in Excel.
Using the array of computernames from above, iterate through them
$result = $computers | ForEach-Object {
# test if the computer can be reached
if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
$installId = Invoke-Command -ComputerName $_ -ScriptBlock {
cscript.exe //nologo "$env:SystemRoot\System32\slmgr.vbs" -dti
}
# $installId is returned as array !
# output an object with two properties
[PsCustomObject]#{
Computer = $_
InstallId = $installId[0] -replace '\D' # remove everything non-numeric
}
}
else {
Write-Warning "Computer $_ is not responding"
}
}
# now you can display the result on screen
$result | Format-Table -AutoSize
# or by means of the GridView if you prefer
$result | Out-GridView -Title 'Computer InstallIds'
# and save the results in your central share as structured CSV file
$result | Export-Csv -Path '\\server\share\restofpath\ComputerInstallIds.csv' -NoTypeInformation
You may have to append -Credential $adminCreds to the Invoke-Command call to make sure you have permissions to have each machine run that piece of code in the scriptblock. The easiest way of obtaining that credential is to start off with $adminCreds = Get-Credential -Message "Please enter administrator credentials"

How do you store a list of users (C:\Users) in PowerShell to use later on in the same script?

I am having issues trying to figure out how to make this script work. I need to get a list (either stored in powershell somehow or saved to a text file) of users from the C:\Users folder and then run a script deleting certain cache data in each of those users folders. Because I am running this based off of a list that I pulled from AD (into another .txt file), I am having trouble understanding how to run it based off of a foreach, foreach concept where there are two foreach statements in one script.
$Computers = Get-Content "C:\Temp\Cache Cleanup Project\June 10 Lists\ComputerUp.txt" | foreach ($Computer in $Computers) {
dir \\$Computer\C$\Users | select Name | Out-File "C:\temp cache cleanup project\Computer Users\$Computer.txt"
I feel like I am missing something in the script above. What I need is a folder filled with .txt files with the name of each computer name that is in the ComputerUp.txt file. In each file will be a list of users for that specific machine. My other scripts will need to reference each of these files in order to go through and clear out the cache'd files.
I hope this makes sense. I have to clean up caches on near 700 machines and each user profile has to be scrubbed and this was the best way I could figure out how to do it. If anyone has any better suggestions, I am all ears.
What I need is a folder filled with .txt files with the name of each computer name that is in the ComputerUp.txt file
Using the file system for this is unnecessary - you can store this information in a variable in-memory instead:
# Read the list of computers from disk
$Computers = Get-Content .\path\to\ComputerUp.txt
# Create a dictionary to hold ComputerName->ListOfUsers data
$UsersPerComputer = [ordered]#{}
foreach($computer in $Computers){
# enumerate the remote folder names
$listOfNames = Get-ChildItem \\$Computer\C$\Users |Select-Object -ExpandProperty Name
# assign the list to the dictionary, use the computer name as the key
$UsersPerComputer[$computer] = $listOfNames
}
Now you can retrieve the list for a single computer by name:
$targetMachine = 'Computer123'
$UsersPerComputer[$targetMachine] # this will resolve to the list of user folder names we got from Computer123
Seems like you're confusing foreach with ForeEach-Object.
With foreach your script would look like this:
$Computers = Get-Content "C:\Temp\Cache Cleanup Project\June 10 Lists\ComputerUp.txt"
foreach ($Computer in $Computers)
{
(Get-ChildItem "\\$Computer\C$\Users").Name |
Out-File "C:\temp cache cleanup project\Computer Users\$Computer.txt"
}
With ForEach-Object it would look like this:
Get-Content "C:\Temp\Cache Cleanup Project\June 10 Lists\ComputerUp.txt" | ForEach-Object {
(Get-ChildItem "\\$_\C$\Users").Name |
Out-File "C:\temp cache cleanup project\Computer Users\$_.txt"
}
As a side note, doing this should get the job done exponentially faster:
$Computers = Get-Content "C:\Temp\Cache Cleanup Project\June 10 Lists\ComputerUp.txt"
$result = Invoke-Command -ComputerName $Computers -ScriptBlock {
Get-ChildItem C:\Users
}
$result | Group-Object PSComputerName | ForEach-Object {
$_.Group.Name | Out-File "C:\temp cache cleanup project\Computer Users\$($_.Name).txt"
}

Adding each item from for loop to csv file

I am trying to use power shell to determine whether a server has a particular patch installed based on the KB and if not append the name to a csv. my input file has system names so I want to export that system name if it does not find the patch installed.
here is what i have so far. The export to csv part does not seem to work.
forEach-Object{
try{
$status = wmic /node:#sys.csv qfe list full /format:table | findstr /i $kb_number
if(!$status){
$output_file = New-Item C:\temp\$kb_number.csv -ItemType File
export-csv $output_file -append -Force
}
else{
write-output $status
}
}
catch{
$error_message = $_.Exception.Message
#write-output "the error message is" $error_message
write-output "Could not find any system with this patch installed."
}
}
Why your code might be failing
We don't see where you're setting the values of #sys.csv or $kb_number in the code you shared. Either of those could be throwing you off.
But the real issue is Export-Csv. For one, you're making a new CSV with every iteration of the loop. And for two, you have to pass in some item for the cmdlet to export as a CSV. Right now, you're only providing these values.
$output_file = New-Item C:\temp\$kb_number.csv -ItemType File
Export-csv -Path $output_file -append -Force
Export-Csv requires an input object. You're not giving it one now.
What do you want to export? If you just want a list of computers without a patch, do this instead.
if(-not(Test-path C:\temp\$kb_number.csv)){
#if file doesn't exist, make it
$output_file = New-Item C:\temp\$kb_number.txt -ItemType File
}
#adds computer name if it doesn't have the patch
Add-Content -Path $output_file -Value $computer
General Suggestions
Instead of using ForEach-Object, you might find it's easier to debug if you use a ForEach loop like this.
$computers = Get-Content C:\pathTo\Your\ComputerList.txt
ForEach($computer in $computers){
}
One additional source of trouble is that your code is using older dos commands in WMIC and then tries to use PowerShell to store the records. You don't need to do this and can make it easier on yourself if you swap out the calls to wmic for Get-WmiObject or Get-CimInstance, the PowerShell native versions of the commands.
To do that, change this line:
wmic /node:#sys.csv qfe list full /format:table | findstr /i $kb_number
translates into
$kb_number = "KB4576484"
Get-CimInstance Win32_QuickFixEngineering -Filter "HotfixID = '$kb_number'" -ComputerName $computer
Source Description HotFixID InstalledBy InstalledOn
------ ----------- -------- ----------- -----------
Update KB4576484 NT AUTHORITY\SYSTEM 9/14/2020 12:00:00 AM
You can store the output of that in a variable and then call Export-Csv on it and that should work.
When in doubt, remove the filter part and just get it working to export all patches to a csv. Then add complexity by adding back the filtering statements.

exporting Powershell Script to CSV

We are getting ready to merge our AD with another. We have about 300 computers that I'm trying to match up with who uses them so the accounts and home folders migrate correctly, and I'm trying to think of the most efficient way to get this information.
We have everyone in an inventory system (Filemaker) (and will be implementing SCCM once we migrate (thank god) ) but we had a few errors when we did our first test batch. Im looking for something I can push out through group policy (possibly?) that will give me the computer name, logged in account, and them email it to me.
So far this is what I have.
[System.Environment]::UserName
[System.Environment]::UserDomainName
[System.Environment]::MachineName
Out-File T:\TEST.txt
But the output is blank. Any idea what I'm doing wrong here? Also is there a way to have this run on multiple computers but write to the same file?
"$env:USERNAME,$env:USERDOMAIN,$env:COMPUTERNAME" | Out-File 'T:\test.txt'
will write the name and domain of the currently logged-in user as well as the hostname of the local computer to the file T:\test.txt.
Using a single file may cause conflicts due to concurrent write attempts, though. It's better to use one file per computer, like this:
"$env:USERDOMAIN\$env:USERNAME" | Out-File "T:\$env:COMPUTERNAME.txt"
Run it as a logon script (or from a logon script), e.g. like this:
powershell -ExecutionPolicy Bypass -File "\\%USERDNSDOMAIN\netlogon\your.ps1"
Get-ADComputer -Filter * -Property * | Select-Object Name | Out-File C:\outdir\machinelist.txt -NoTypeInformation -Encoding UTF8
will get you all machine names, unless you have them already. Either way, use your list of machines in
$MachineList = Get-Content -Path c:\outdir\machinelist.txt;
foreach ($Machine in $MachineList){
($Machine + ": " + #(Get-WmiObject -ComputerName $Machine -Namespace root\cimv2 -Class Win32_ComputerSystem)[0].UserName) | Out-File "C:\outdir\result.txt" -Append
}
If you change the destination directory to somewhere that all computers have access to, it can run on multiple computers. It won't email it to you but you can just grab it.
You'll need to pipe those properties into the file like..
[System.Environment]::UserName, [System.Environment]::UserDomainName, [System.Environment]::MachineName | Out-File T:\Test.txt