Using Powershell Get-ItemProperty through all of AD computers object - powershell

I'm a complete newbie in Powershell (and programming as you may have guessed), I want to get the result of the following PS command for each of our AD computer object and print the result in a text file...but I'm completely lost. Does anyone have a lifeline I could hold on to?
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-String -Pattern "mysoftwarename"
Thank you very much.

$ScriptBlock = {Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-String -Pattern "mysoftwarename"}
$Computers = (Get-ADComputers -filter * ).name
$Creds = (Get-Credential)
foreach ($Computer in $Computers)
{
"`n`n$Computer`n" >> .\file.txt # "`n" just emulates Enter key press
Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -Credential $Creds >> .\file.txt
}
This will work fine if you have all your computers online and PS remoting configured properly. Otherwise, it will require modifications.

Related

How to find PST files on remote computers using Powershell script

I need some help. I cant’t find what’s wrong with my PowerShell script.
The goal is quite simple. I have to find (.*pst)-files on the users profile on domain computers in the network. Location to search is “C:\Users\”.
List of the PC names where exported to listcomputer.txt. The trouble is the script run with no errors and no message at all.
$computers = Get-Content c:\temp\listcomputer.txt
$filePath = "C:\Users\"
foreach($computer in $computers)
{
if(Test-Connection -ComputerName $computer -Quiet)
{
Invoke-Command -ComputerName $computer -ScriptBlock
{Get-ChildItem -Path $filePath -Recurse -Include '*.pst, *.ost'} }}
First of all I’ve to check connectivity to hosts by Test-Connection cmdlet.
Separately each of the command run successfully. I've tried it.
For example: Test-Connection -ComputerName $computer
runs with “true” result and it’s OK.
Also
Invoke-Command -ComputerName $computer -ScriptBlock {Get-ChildItem -Path $filePath -Recurse -Include '*.pst'} The result is displayed data with information about files were find in folders.
But all together run with no visible result in the PowerShell console console view result
Regards!
.pst can be located anywhere, even other drives, or attached storage. You are only looking for C:\.
So maybe this refactor to hit all potential connected drives.:
Get-Content -Path 'c:\temp\listcomputer.txt' |
ForEach-Object {
if(Test-Connection -ComputerName $PSItem -Quiet)
{
Invoke-Command -ComputerName $PSItem -ScriptBlock {
(Get-CimInstance -ClassName Win32_LogicalDisk).DeviceID |
ForEach-Object {
If ($PSItem -eq 'C:')
{Get-ChildItem -Path "$PSItem\Users" -Recurse -Include '*.pst, *.ost'}
Else {Get-ChildItem -Path $PSItem -Recurse -Include '*.pst, *.ost'}
}
}
}
}

Remote management with powershell

I'm trying to get some information from several machines on the network but I get loads of entries of the local machine.. for each entry in the text file I get an entry from the local machine.
Any idea where I'm going wrong.. winrm is configured on the remote machines and running.
$Username = Read-Host "Please enter Username"
$Password = read-host "please enter Password"
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass
$computers = gc c:\test\file.txt
foreach ($Computer in $computers)
{
Invoke-command -ComputerName $computers -credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | out-file c:\test\output.txt -append}
}
cls
Thanks in advance :)
Invoke-Command will take an array for the ComputerName param so you can use $computers instead of using a foreach loop (assuming that you have one computer name per-line in the file).
I've also used Get-Credential to prompt for the full credential in one go rather than asking for username and password individually.
$Cred = Get-Credential
$computers = Get-Content c:\test\file.txt
Invoke-Command -ComputerName $computers -Credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | Out-File c:\test\output.txt -Append}
The reason you are only seeing a single computers info in c:\test\output.txt is because the output of the the ipconfig command is being saved to the remote computer... so you will have a c:\test\output.txt file on each computer you run the command against.
EDIT:
To take the output of each remote command and save it to your local computer just move the Out-File outside the Invoke-Command like this:
$Cred = Get-Credential
$computers = Get-Content c:\test\file.txt
Invoke-Command -ComputerName $computers -Credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'"} | Out-File c:\test\output.txt -Append
The issue is you are iterating one by one but you are not passing one by one to the invoke-command, $computer will have each value at a time in the foreach loop.
Instead of this:
foreach ($Computer in $computers)
{
Invoke-command -ComputerName $computers -credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | out-file c:\test\output.txt -append}
}
Do this:
foreach ($Computer in $computers)
{
Invoke-command -ComputerName $computer -credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | out-file c:\test\output.txt -append}
}
Further improvement:
You do not have to give Invoke-Expression -Command:"cmd.exe /c 'ipconfig'"
Instead of this,you can directly use ipconfig inside the scriptblock.

Getting a specific service recovery option for a list of servers

Apologies in advance for errors. I'm still learning Powershell.
I'm trying to check a specific service recovery options for a list of servers in AD
$credential = Get-Credential
$servers = Get-ADComputer -Filter * -properties * | ?{$_.OperatingSystem -match "server"} | ft name -hidetableheaders | out-string
$Results = #()
foreach ($Server in $Servers)
{
Invoke-command -cn $server -credential $credential -ScriptBlock {Get-WMIObject win32_service |
Where-Object {$_.description -imatch "nscli" -and $_.startmode -eq "Auto"}; foreach ($service in $services){sc.exe qfailure $service.name}}
}
I'm getting the following error
Invoke-command : One or more computer names are not valid. If you are trying to pass a URI, use the -ConnectionUri parameter, or pass URI objects
instead of strings.
At line:1 char:32
+ foreach ($Server in $Servers) {Invoke-command -cn $server -credential $credentia ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (System.String[]:String[]) [Invoke-Command], ArgumentException
+ FullyQualifiedErrorId : PSSessionInvalidComputerName,Microsoft.PowerShell.Commands.InvokeCommandCommand
If I run the command directly on each server, I don't have any issues.
Invoke-command -cn EXCHANGE -credential $credential -ScriptBlock {$services = Get-WMIObject win32_service | Where-Object {$_.description -imatch "nscli" -and $_.startmode -eq "Auto"}; foreach ($service in $services){sc.exe qfailure $service.name}}
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: NSClientpp
RESET_PERIOD (in seconds) : 0
REBOOT_MESSAGE :
COMMAND_LINE :
FAILURE_ACTIONS : RESTART -- Delay = 120000 milliseconds.
RESTART -- Delay = 120000 milliseconds.
Because I'm using sc.exe, I'm unsure how to output the list into a csv format but at least, I can get some information of which servers the service failure restart aren't set accordingly
Thanks in advance
Cheers
G
Since Invoke-Command doesn't support the -WhatIf switch, your best bet is to echo the server name on console to see the server name that causes failure. In the debugging statement, the server name is enclosed withing single quotes ' for, say, extra whitespace is easier to see. Like so,
foreach ($Server in $Servers)
{
Write-host $("Now processing server '{0}' " -f $Server)
Invoke-command ...
}
You could also try to write the Invoke-Command statements to console and copy-paste those to another a Powershell session to see which, if any, is the failing one.
For what it is worth, you seem to use variables $Server and $server (note the upper/lowercase difference). Though variable names are not case sensitive in Powershell, you might have uninitialized variables caused by simple typos. To guard against such, use Set-StrictMode. It will cause Powershell to complain about uninitialized variables, which commonly are caused by mistyping variable names.
Got it working. Should have responded earlier. Thanks guys.
# Get the credential
$Credential=Get-Credential
# Collect the server list
$Servers = Get-ADComputer -Filter * -Properties operatingsystem | Where operatingsystem -match 'server'
$Results = #()
## Check service status
$Results = foreach ($Server in $Servers)
{Invoke-Command -ComputerName $Server.Name –Ea 0 -Credential $Credential {Get-Service |?{$_.DisplayName –match “nscli”} }}
## Create a csv report
$Results| Sort PSComputerName | select PSComputerName, DisplayName, Name, Status |Export-Csv nsclient_service.csv -NoTypeInformation -UseCulture
# Check the recovery options for NSclient service on each Server
foreach ($Server in $Servers)
{ write-host "Checking " $Server.Name;
Invoke-Command -ComputerName $Server.Name –Ea 0 -Credential $Credential `
{$services = Get-WMIObject win32_service | Where-Object {$_.name -imatch "nscli" -and $_.startmode -eq "Auto"}; `
foreach ($service in $services){sc.exe qfailure $service.name}}
}

Stopping & Restarting Services Remotely Using Set-Service

I've got a list of 10-15 services that I routinely need to restart on 6 servers. I have a script that calls a list of services, then calls a list of the servers, and then stops all the services:
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
Get-Service -Name $Services -ComputerName $Machines | Set-Service -Status Stopped
I then have another separate script to start them up again:
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
Get-Service -Name $Services -ComputerName $Machines | Set-Service -Status Running
I've checked around and can't seem to find a way of putting this into a single script. As I understand, Set-Service only has the ability to Stop, Start & Pause services, not restart them at the same time.
Any ideas? I might be missing something completely obvious.
To restart services simply use Restart-Service:
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
Get-Service -Name $Services -ComputerName $Machines | Restart-Service
Since according to the comments PowerShell v6 has removed support for remote access from the *-Service cmdlets you need to resort to Invoke-Command for remote execution when running v6 or newer, like this:
Invoke-Command -Computer $Machines -ScriptBlock {
Get-Service -Name $using:Services -ErrorAction SilentlyContinue |
Restart-Service
}
or like this:
Invoke-Command -Computer $Machines -ScriptBlock {
Restart-Service $using:Services -ErrorAction SilentlyContinue
}
Another option would be WMI:
$fltr = ($Services | ForEach-Object { 'Name="{0}"' -f $_ }) -join ' or '
Get-WmiObject Win32_Service -Computer $Machines -Filter $fltr | ForEach-Object {
$_.StopService()
$_.StartService()
}
I am with Ansgar, this should work
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
foreach ($service in $services){
foreach ($computer in $Machines){
Invoke-Command -ComputerName $computer -ScriptBlock{
Restart-Service -DisplayName $service}
}
}
it is a little messy but should give you a starting point
Sorry I forgot to take time to explain what is going on, so you import each of your txt docs and then it will process for each service and each computer and restart the services.
You can try this single liner command:
Get-Content .\services.txt | %{Get-WmiObject -Class Win32_Service -ComputerName (Get-Content .\computers.txt) -Filter "Name='$_'"} | %{$_.StopService()}; Get-Content .\services.txt | %{Get-WmiObject -Class Win32_Service -ComputerName (Get-Content .\computers.txt) -Filter "Name='$_'"} | %{$_.StartService()}

Powershell passing arguments in ScriptBlock

I'm trying to get the last write time on a file from a remote server.
This doesn not work:
$server = "MyServerName"
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {Get-ChildItem "\\$args[0]\hot.war" } -argumentlist $server | select -Property LastWriteTime
This does work:
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {Get-ChildItem "\\MyServerName\hot.war" } -argumentlist $server | select -Property LastWriteTime
Can anyone help make the first set work?
Be careful with variables in strings: "\\$args[0]\hot.war" will be expanded to \\MyServerName[0]\hot.war.
Use "\\$($args[0])\hot.war" to be sure that $args[0] will be treated as a single expression.
See: http://blogs.msdn.com/b/powershell/archive/2006/07/15/variable-expansion-in-strings-and-herestrings.aspx
Another way, if you are using PowerShell 3. You can do something like this:
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {
Get-ChildItem "\\$using:server\hot.war"
} | select -Property LastWriteTime
You will want to add the server variable into your first line...
$server = "MyServerName"
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {Get-ChildItem "\\$server\hot.war" } -argumentlist $server | select -Property LastWriteTime