I have a simple script below to give me the local admin group membership for all my servers which I have in a text file.
I get the desired result with no issues in the console but I am looking for a way to have this exported to a CSV file somehow.
Any ideas?
$server = Get-Content 'C:\powershell\Scripts\GetLocalAdmin\APAC+Sri
Lanka\servers.txt'
foreach($item in $servers){
write-host "`n"
$servername = Write-Host "Checking $($item.toupper())" -ForegroundColor
Green
write-host "`n"
$invoke = invoke-command {net localgroup Administrators} -comp $item
}
You can use the Export-CSV cmdlet. Note that you either have to create an array to store every return value from your Invoke-Command using $invokes += ... or just use this:
Get-Content 'C:\powershell\Scripts\GetLocalAdmin\APAC+SriLanka\servers.txt' |
ForEach-Object {
invoke-command {net localgroup Administrators} -comp $_ |
Export-Csv 'your_csv_path.csv'
}
Related
I am using powershell to pull basic computer information from all computers on a LAN. These computers are not, and will never be on, a domain. I have had some success in my test runs getting the output for all of the machines to save into the c:\scripts folder on the host machine. I am, however, having to use the Invoke-command for every cmdlet so that I can put the Output destination outside of the {}.
$computers = Get-Content -Path 'c:\scripts\livePCs.txt'
foreach ($computer in $computers) {
$username = '$computer\Administrator'
$password = Get-Content 'C:\scripts\encrypted_password.txt' | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PSCredential($username, $password)
# Local Mounted Shares Enumeration
Invoke-Command -Computername $computer -Credential $credential {Get-SmbMapping | Format-Table -AutoSize} | Out-File "c:\scripts\ComputerInformation\$computer.mountedshares.ps.txt"
# Local Shares Enumeration
Invoke-Command -Computername $computer -Credential $credential {Get-SmbShare | Format-Table -AutoSize} | Out-File "c:\scripts\ComputerInformation\$computer.localshares.ps.txt"
I would prefer not to have to do this and it becomes problematic when I have to use If/Else statements, where, because I cannot put the destination outside of braces, I get a file cannot be found error (since it is trying to save on the remote host). I tried using a share instead, in the below but now am getting an access to the file path is denied error.
Invoke-Command -Computername $computer -Credential $credential {
$computername = hostname.exe
If ($PSVersionTable.PSVersion -ge '4.0') {
If (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Get-WindowsOptionalFeature -Online -FeatureName *Hyper-V* | Format-Table -AutoSize | Out-File -Width 1024 "\\$env:computername\scripts\ComputerInformation\$computername.hyperv.admin.ps.txt"
if (Get-Command Get-VM -ErrorAction SilentlyContinue) {
Get-VM | Format-Table -AutoSize | Out-File -Width 1024 -Append "c:\scripts\ComputerInformation\$computername.hyperv.admin.ps.txt"
Get-VM | Get-VMNetworkAdapter | Format-Table -AutoSize | Out-File -Width 1024 -Append "c:\scripts\ComputerInformation\$computername.hyperv.admin.ps.txt"
} else {
Write-Host -ForegroundColor Yellow " Hyper-V feature not installed on this host"
}
} else {
Write-Host -ForegroundColor Red " You do not have required permissions to complete this task ..."
}
} else {
Write-Host -ForegroundColor Red " This commands requires at least PowerShell 4.0 ... manual inspection is required"
}
How do I run this one a remote machine using Invoke-Command but save the output to the local c:\scripts folder?
If the goal is to give yourself the option to output to multiple files on the calling system, you could use a hash table ($results) inside of your script block to store your results. Then output that table at the end of your script block. Based on those keys/values, you could output to file.
foreach ($computer in $computers) {
$Output = Invoke-Command -Computername $computer -Credential $credential {
$results = #{}
$computername = hostname.exe
If ($PSVersionTable.PSVersion -ge '4.0') {
If (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
$HyperV = Get-WindowsOptionalFeature -Online -FeatureName *Hyper-V* | Format-Table -AutoSize
if (Get-Command Get-VM -ErrorAction SilentlyContinue) {
$VMInfo = Get-VM | Format-Table -AutoSize
$VMNic = Get-VM | Get-VMNetworkAdapter | Format-Table -AutoSize
} else {
Write-Host -ForegroundColor Yellow " Hyper-V feature not installed on this host"
}
} else {
Write-Host -ForegroundColor Red " You do not have required permissions to complete this task ..."
}
} else {
Write-Host -ForegroundColor Red " This commands requires at least PowerShell 4.0 ... manual inspection is required"
}
$results.Add('HyperV',$HyperV)
$results.Add('VMInfo',$VMInfo)
$results.Add('VMNic',$VMNic)
$results
}
$Output.HyperV | Out-File -Width 1024 "c:\scripts\ComputerInformation\$computer.hyperv.txt"
$Output.VMInfo | Out-File -Width 1024 "c:\scripts\ComputerInformation\$computer.VMInfo.txt"
$Output.VMNic | Out-File -Width 1024 "c:\scripts\ComputerInformation\$computer.VMNic.txt"
}
If the goal is to simply output all data to one location, you can simply store your Invoke-Command result into a variable. Then write the variable contents to file:
$Output = Invoke-Command -Computername $computer -Scriptblock { # my code runs here }
$Output | Out-File "C:\Folder\$computer.txt"
If you are looking to capture Write-Host output in a variable, you will need to send the information stream to the success stream ( { script block } 6>&1 }
You can redirect the output
$YourScriptBlock = { your script }
$result = Invoke-Command -ScriptBlock $YourScriptBlock 4>&1 -Computer $c -Credential $c
Afterwards the contents are in $result
write-output $result
Have a bit of an issue whereby would like to figure out the best way to handle success or failures. Have a powershell query which checks the dcom port range, if it is within the specified value output to a success file, if not a failure file. The issue is, it seems to be outputting the entire serverlist.txt for a success and need to know a way to break this down so it only appends a server (either success/failure) to it, not all at once.
Here is the powershell script contents:
powershell -executionpolicy bypass .\DCOMPortRange.ps1
Where DCOMPortRange.ps1 contains
$computername = Get-Content -Path "C:\Folderpath\serverlist.txt"
$val = (Get-ItemProperty "hklm:SOFTWARE\Microsoft\Rpc\Internet") | Select-Object -ExpandProperty Ports
if($val -eq "50000-50500")
{
Write-Output "$computername" | out-file C:\folderpath\Success.log -append
} Else {
Write-Output "$computername" | out-file C:\folderpath\Failure.log -append
}
The issue is the error path lets say is a success it appends the entire server list.
Please advise?
This is how I would do it. This does require that you do have PSremoting enabled on the servers
$computername = Get-Content -Path "C:\Folderpath\serverlist.txt"
ForEach ($server in $computername) {
$val = Invoke-Command -Computername $server -ScriptBlock {(Get-ItemProperty "hklm:SOFTWARE\Microsoft\Rpc\Internet") | Select-Object -ExpandProperty Ports}
if ($val -ge 50000 -and $val -le 50500) {
Write-Output "$server" | out-file C:\folderpath\Success.log -append
}
Else {
Write-Output "$server" | out-file C:\folderpath\Failure.log -append
}
}
Edit: A change to the if statement
/Anders
$remotecomputername = #("PC1","PC2","RealServerName")
ForEach ($computer in $remotecomputername) {
Invoke-Command -Computername $computer -ScriptBlock { $val = (Get-
ItemProperty "hklm:SOFTWARE\Microsoft\Rpc\Internet") | Select-Object -
ExpandProperty Ports} }
if($val -eq "50000-50500") {
write-host $computer DCOM Port in Range
} else {
write-host $computer DCOM Port not in range
}
The idea is to search for a pattern on several servers with Select-String and Invoke-Command.
I am not able to get the $results back to the local server correctly and print it either in a file and/or also in the console (this is not so important).
What I need is to be able to see the results of the search (filename, line, match)
Set Execution-Policy RemoteSigned
$servidores = Get-Content "C:\ServerList.txt"
(Get-Date).ToString()
Write-Output "----------------Running script------------------"
Write-Output "---------------Servers in scope :---------------"
Write-Output $servidores
Write-Output "-----------------Starting Loop-----------------"
$ToExecute = {
Select-String -SimpleMatch "password" -Path C:\*.* -Exclude C:\Users\Public\resultados.txt
}
foreach ($server in $servidores){
$result = Invoke-Command -ComputerName $server -ScriptBlock $ToExecute
Write-Output "----------Executing Search on Server:-----------"
(Get-Date).ToString();$server;
Write-Output "------------------------------------------------"
Write-Output $result
Out-File $result C:\Users\Public\resultados.txt
}
For Out-File if you are not piping, you will need to use the -inputobject flag. Because Out-File does not take the inputobject by position.
Out-File -InputObject $result -path C:\Users\Public\resultados.txt
Otherwise you could use Tee-Object to replace the write-output/outfile.
$result | Tee -filepath C:\Users\Public\resultados.txt
Set Execution-Policy RemoteSigned
$servidores = Get-Content "C:\ServerList.txt"
Write-Output ("----------------Running script-----"+(Get-Date).ToString()+"-- -----------")
Write-Output "--------------------------Servers in scope----------------------------"
Write-Output $servidores
foreach ($server in $servidores){
Write-Output ("---Executing Search on Server:---"+$server+"----at:"+(Get- Date).ToString()+"-------------")
$result= Invoke-Command -ComputerName $server -ScriptBlock {Select-String - Pattern "password" -Path C:\*.txt -AllMatches}
if ($result -ne $null){
Write-Output $result.ToString()
}
}Read-Host -Prompt "Press Enter to exit"
The code below turns off firewall on each remote computers and return any computers that was turned off. I am also trying to retrieve software that has been authorized to pass through firewall for each computer.
I understand that I am using try, catch so is there any way to print the output of $Appfilter to offComp&programsALLO.txt ? The text file just prints the value of $Appfilter.
The output should ideally look like:
Computers:
"name of computer" followed by "programs allowed"
Here is the code:
Get-ADComputer -Filter * | Select-Object -ExpandProperty Name | Out-File .\ADcomputers.txt
$LaunchLine = 'powershell.exe -Version 4.0 -Command "& {netsh advfirewall set allprofiles state off}"'
$Appfilter = 'powershell.exe -Version 4.0 -Command "& {Get-NetFirewallApplicationFilter -program * | fl program}"'
$ComputerList = Get-Content .\adcomputers.txt
foreach($Computer in $ComputerList) {
[String]$wmiPath = "\\{0}\root\cimv2:win32_process" -f $computer
try {
[wmiclass]$Executor = $wmiPath
$executor.Create($LaunchLine, $Appfilter)
} catch {
Add-Content offComp&programsALLO.txt "computers:$Computer, $Appfilter "
}
}
I would use Invoke-Command with the -ComputerName parameter if possible:
#store AD Computer names in an array
$computerList = (Get-ADComputer -Filter *).Name
#declare results arrays
$results = #()
$offline = #()
#for each computer
foreach($computer in $computerList) {
#if computer responds to ping
if(Test-Connection $computer -Count 2 -Quiet -ErrorAction SilentlyContinue) {
#disable firewall
Invoke-Command -ComputerName $computer -ScriptBlock {
netsh advfirewall set allprofiles state off
} | Out-Null
#store retrieved authorized programs list in an array
$programs = Invoke-Command -ComputerName $computer -ScriptBlock {
(Get-NetFirewallApplicationFilter).Program
}
#build results object and add it to results array
$results += [PSCustomObject]#{
ComputerName = $computer
Programs = $programs -join ";"
}
} else {
#build results object and add it to offline array
$offline += [PSCustomObject]#{
ComputerName = $computer
Status = "OFFLINE"
}
}
}
#export results to files
$results | Out-File "report.txt"
$offline | Out-File "offline.txt"
I am trying to get specific KBXXXXXX existence on a list of servers , but once my script one server it takes time and return result and come back and then move to next one . this script works perfectly fine for me .
I want my script to kick off and get-hotfix as job and other process just to collect the results and display them.
$servers = gc .\list.txt
foreach ($server in $servers)
{
$isPatched = (Get-HotFix -ComputerName $server | where HotFixID -eq 'KBxxxxxxx') -ne $null
If ($isPatched)
{
write-host $server + "Exist">> .\patchlist.txt}
Else
{
Write-host $server +"Missing"
$server >> C:\output.txt
}
}
The objective it to make the list execute faster rather than running serially.
With Powershell V2 you can use jobs as in #Andy answer or also in further detail in this link Can Powershell Run Commands in Parallel?
With PowerShell V2 you may also want to check out this script http://gallery.technet.microsoft.com/scriptcenter/Foreach-Parallel-Parallel-a8f3d22b using runspaces
With PowerShell V3 you have the foreach -parallel option.
for example (NB Measure-Command is just there for timing so you could make a comparison)
Workflow Test-My-WF {
param([string[]]$servers)
foreach -parallel ($server in $servers) {
$isPatched = (Get-HotFix -ComputerName $server | where {$_.HotFixID -eq 'KB9s82018'}) -ne $null
If ($isPatched)
{
$server | Out-File -FilePath "c:\temp\_patchlist.txt" -Append
}
Else
{
$server | Out-File -FilePath "c:\temp\_output.txt" -Append
}
}
}
Measure-Command -Expression { Test-My-WF $servers }
For this use PowerShell jobs.
cmdlets:
Get-Job
Receive-Job
Remove-Job
Start-Job
Stop-Job
Wait-Job
Here's an untested example:
$check_hotfix = {
param ($server)
$is_patched = (Get-HotFix -ID 'KBxxxxxxx' -ComputerName $server) -ne $null
if ($is_patched) {
Write-Output ($server + " Exist")
} else {
Write-Output ($server + " Missing")
}
}
foreach ($server in $servers) {
Start-Job -ScriptBlock $check_hotfix -ArgumentList $server | Out-Null
}
Get-Job | Wait-Job | Receive-Job | Set-Content patchlist.txt
Rather than use jobs, use the ability to query multiple computer that's built into the cmdlet. Many of Microsoft's cmdlets, especially those used for system management, take an array of strings as the input for a -Computername parameter. Pass in your list of servers, and the cmdlet will query all of them. Most of the cmdlets that have this ability will query the servers in series, but Invoke-Command will do it in parallel.
I haven't tested this as I don't have Windows booted at the moment, but this should get you started (in sequence).
$servers = gc .\list.txt
$patchedServers = Get-HotFix -ComputerName $servers | where HotFixID -eq 'KBxxxxxxx'|select machinename
$unpatchedServers = compare-object -referenceobject $patchedServers -differenceobject $servers -PassThru
$unpatchedServers |out-file c:\missing.txt;
$patchedServers|out-file c:\patched.txt;
In parallel:
$servers = gc .\list.txt
$patchedServers = invoke-command -computername $servers -scriptblock {Get-HotFix | where HotFixID -eq 'KBxxxxxxx'}|select -expandproperty pscomputername |sort -unique
As before, I don't have the right version of Windows available at the moment to test the above & check the output but it's a starting point.