Make function for workflow - Powershell - function

I'm trying to speed up script for checking disk SMART prefail status, because I need to check two thousand computers.
However the script is still writing out status for the same disk - computers "hostname1" and "hostname2" has different disks.
function disk-status (){
param(
[Parameter(Mandatory=$true)][string]$computername
)
$WMI = Get-WMIObject -Class Win32_DiskDrive
ForEach ($Drive in $WMI){
$disk = $Drive.Caption
$status = $Drive.Status
#condition will be changed to "-notmatch"
if ($status -match "OK"){
#I'm using write-output to see if the script works during testing
Write-output $computername $disk $status
}
}
}
workflow Get-disk-status {
param(
[string[]]$computers
)
foreach -parallel ($computer in $computers) {
disk-status -computername $computer
}
}
#in the final version I'm going to use get-adcomputer
$computers = "hostname1", "hostname2"
Get-disk-status $computers
Output I get:
hostname1
ST500LM0 21-1KJ152 SCSI Disk Device
OK
hostname2
ST500LM0 21-1KJ152 SCSI Disk Device
OK
Can anybody give me at least a hint how to fix it?
Thank you in advance!

Try changing
$WMI = Get-WMIObject -Class Win32_DiskDrive
to
$WMI = Get-WMIObject -Class Win32_DiskDrive -ComputerName $computername
It looks like it may be retrieving information from the machine you are on because you haven't passed a computer to the Get-WMIObject cmdlet.

Related

powershell variable into where-object does not return data

I am writing a script to ultimately check a block of servers for a certificate by FriendlyName and then go back and delete them once confirmed. Right now I am just trying to get the initial check to work. Currently it is not returning any data. Can anyone help?
$ContentsPath = "C:\Servers.txt"
$Servers = Get-Content $ContentsPath
$CertDeletionFile = "C:\CertsDeleted.csv"
$Today = Get-Date
$Certificate = Read-Host -Prompt "What certificate would you like to
REMOVE?"
write-host $Certificate
function findCert {
param ([string]$Certificate)
Invoke-Command -ComputerName $Servers -ScriptBlock {Get-Childitem -Path
Cert:LocalMachine\My | where {$_.friendlyname -eq $Certificate } | Select-
Object -Property FriendlyName }
}
findCert
As Mathias R. Jessen comments, your findcert function needs a certificate name as a parameter, and you aren't passing anything when you call it, so it won't run properly.
You're also trying to use a local computer variable $Certificate, on a remote computer inside an invoke-command, and the remote computer can't get to that variable across the remoting.
I've rewritten it, with $using: which is a syntax that tells PS to send the value over the remoting session, and with renamed variables so it's more clear which part is accessing which variables:
$ContentsPath = 'C:\Servers.txt'
$Servers = Get-Content -LiteralPath $ContentsPath
$CertDeletionFile = 'C:\CertsDeleted.csv'
$Today = Get-Date
$typedCertificateName = Read-Host -Prompt "What certificate would you like to
REMOVE?"
write-host $typedCertificateName
function findCert {
param ([string]$Certificate)
Invoke-Command -ComputerName $Servers -ScriptBlock {
Get-Childitem -Path Cert:LocalMachine\My |
where-Object {$_.friendlyname -eq $using:Certificate } |
Select-Object -Property FriendlyName
}
}
findCert -Certificate $typedCertificateName

cmdlets not recognized on the first run in powershell

I am facing an issue with the first run of powershell code.
cmdlets and user defined function are not recognized in the first run but works fine if I run the code again
user defined function takes values from previous run.i.e. basically we need to run the code twice to get the correct result
Code:
$resultVar=get-CPUAndMemUtilization -computername $computername -CPUCriteria $CPUCriteria -MemCriteria $MemCriteria
#Write-Host "Mme:"$resultVar;
$CPUMem += [PSCustomObject] #{
CPULoad = "$($resultVar[0])"
MemLoad = "$($resultVar[1])"
}
Write-Host $CPUMem;
function get-CPUAndMemUtilization($computername,$CPUCriteria,$MemCriteria)
{
$Memstatus=$null;
$CPUstatus=$null;
$AVGProc = Get-WmiObject -computername $computername win32_processor | Measure-Object -property LoadPercentage -Average | Select Average
$OS = gwmi -Class win32_operatingsystem -computername $computername |
Select-Object #{Name = "MemoryUsage"; Expression = {“{0:N2}” -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/ $_.TotalVisibleMemorySize) }}
$result += [PSCustomObject] #{
ServerName = "$computername"
CPULoad = "$($AVGProc.Average)%"
MemLoad = "$($OS.MemoryUsage)%"
}
if($AVGProc.Average -lt $CPUCriteria)
{
$Memstatus=1;
}else{
$Memstatus=0;
}
if($OS.MemoryUsage -lt $MemCriteria)
{
$CPUstatus=1;
}else{
$CPUstatus=0;
}
$CPUstatus
$Memstatus
return;
}
Code return the System CPU & Me usage of the system in CPU & Mem utilization for a system
Error:
get-CPUAndMemUtilization : The term 'get-CPUAndMemUtilization' is not
recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
You call the function before you import it (so it doesn't exist) into the powershell session, just swap those 2 things:
function get-CPUAndMemUtilization($computername,$CPUCriteria,$MemCriteria)
{
...
}
$resultVar=get-CPUAndMemUtilization -computername $computername -CPUCriteria $CPUCriteria -MemCriteria $MemCriteria
#Write-Host "Mme:"$resultVar;
$CPUMem += [PSCustomObject] #{
CPULoad = "$($resultVar[0])"
MemLoad = "$($resultVar[1])"
}
Write-Host $CPUMem;

try-catch bypassing a step

I have a script that tests connection to a list of servers, and if contactable, gets the status of a service, and puts the results into three variables, $Computer, $Ping (True/False), and $Service (Running or Stopped).
The output is in a hashtable but I can only get to show the servers that ARE contactable, and not the ones that cannot be contactable.
I have placed a try/catch in the $Ping block, as well as -ErrorAction Stop, so that it doesn't attempt to run the $Service script, and instead go to the next $Computer in the array. I think I am trying to do two things at once that are conflicting each other:
add the variables to the #Splat and
don't process any further.
There are actually many more remote registry queries in my script, which will be irrelevant if the $Computer cannot be contactable, but I have shortened it for this post.
Function Get-Ping {
$Servers = (gc "c:\temp\test.txt")
foreach ($Computer in $Servers) {
Write-Host
Write-Host "---------------------------------"
Write-Host "QUERYING $Computer"
Write-Host
Write-Host "Performing ping test..."
try {
$Ping = Test-Connection $Computer -Count 1 -ErrorAction Stop
} catch {
Write-Warning "Cannot Ping $Computer"
Write-Host "Trying next computer..."
Write-Host
continue
}
if ($Ping) {$Ping="$True"}
Write-Host $Computer "can be pinged"
$svcRRStopped = $false
if ($Computer -ne $env:COMPUTERNAME) {
Write-Host "Check RemoteRegistry status..."
}
$svcRR = Get-Service -ComputerName $Computer -Include RemoteRegistry
$SelectSplat = #{
Property = (
'Computer',
'Ping',
'Service'
)}
New-Object -TypeName PSObject -Property #{
Computer=$Computer
Ping=$Ping
Service=$svcRR.status
} | Select-Object #SelectSplat
}
}
$results = Get-Ping
$tableFragment = $results | Select 'Computer','Ping','Service'
$tableFragment
Don't make things more complicated than they need to be.
function Get-Ping {
Get-Content 'C:\temp\test.txt' | ForEach-Object {
$isAvailable = [bool](Test-Connection $_ -Count 1 -EA SilentlyContinue)
if ($isAvailable) {
$rreg = Get-Service -Computer $_ -Name RemoteRegistry |
Select-Object -Expand Status
} else {
$rreg = 'n/a'
}
New-Object -Type PSObject -Property #{
Computer = $_
Ping = $isAvailable
Service = $rreg
}
}
}
Get-Ping
You can simply use the -Quiet Parameter:
Test-Connection $_ -Count 1 -Quiet

Calling a function within a function with invoke-command Powershell

I am hoping to call a function within a function in invoke-command. It looks like I am not doing this properly. I get "get-group_users" is not recognized error. Everything is in one ps1 script.
Truncated Script:
function get-session {
$session = New-PSSession -ComputerName $ip -Credential $cred -Auth CredSSP
$ComputerName = $cred.Replace('\Admin',"")
$csv = "C:\Scripts\Serverlists\" + $ComputerName + ".txt"
$subcomps = import-csv $csv | foreach-object {$_.Name}
foreach ($ComputerName in $subcomps)
{
$ComputerName
$xmlWriter.WriteStartElement("Computer",$ComputerName)
Invoke-Command -Session $session -ScriptBlock { (get-groups_users -ComputerName $ComputerName) } -ArgumentList $ComputerName
$xmlWriter.WriteEndElement()
}
Remove-PSSession -ComputerName $ip
}
$ip = '172.16.24.11'
$ComputerName = "COMP1"
$user = '\Admin'
$cred = $ComputerName + $user
(get-groups_users -ComputerName $ComputerName)
(get-session -Credential $cred -ComputerName $ip)
So, I'm leaving out the get-groups_users function. I can post it if someone thinks it will help explain everything. It is a long one. Also i am leaving out the $xmlwriter definitions.
get-groups_users works when it runs on COMP1, but once I enter PSSession and try and use it in invoke-command for the sub comps it doesn't recognize it.
What am I forgetting?
You have to include the get-groups_users function definition inside the -ScriptBlock parameter when you call Invoke-Command. If you don't, the remote session has no knowledge of the get-groups_users function.
Here's how to fix that:
$ScriptBlock = {
function get-groups_users {
############ Put your function's code here ############
}
(get-groups_users -ComputerName $ComputerName)
};
Invoke-Command -Session $session -ScriptBlock $ScriptBlock -ArgumentList $ComputerName

Powershell function throwing null exception

Okay, so I've read up on calling functions and passing parameters in powershell but maybe I'm not understanding something. I am calling a function and passing a couple parameters and everything works fine. It is variable defined within the function that seems to be null when it isn't.
Function:
function get-session {
$session = New-PSSession -ComputerName $ip -Credential $cred -Auth CredSSP
$subcomps = 'COMP1'
foreach ($Computer in $subcomps)
{
$Computer
Invoke-Command -Session $session -ScriptBlock { Get-WmiObject -ComputerName $Computer -Query "SELECT * FROM Win32_Group" }
}
Remove-PSSession -ComputerName $ip
}
Script:
$ip = '123.45.67.89'
$ComputerName = "HOPCOMP"
$user = '\Admin'
$cred = $ComputerName + $user
$ip
$cred
(get-session -ComputerName $ip -Credential $cred)
When I run this:
Cannot validate argument on parameter 'ComputerName'. The argument is null or empty.
Supply an argument that is not null or empty and then try the command again.
+ CategoryInfo : InvalidData: (:) [Get-WmiObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Of course, if I change $Computer in in the function Get-WMIObject line to COMP1 everything works great. But, the $Computer write out in the foreach loop writes COMP1 successfully.
What am I missing here?
You need to specify the -ArgumentList parameter with your Invoke-Command. For example:
Invoke-Command -Session $session -ScriptBlock {
param($Computer)
Get-WmiObject -ComputerName $Computer -Query "SELECT * FROM Win32_Group"
} -ArgumentList $Computer
The $IP is global and therefore known with the function. The -ArgumentList is needed or populate your $MyScriptBlock a line before acting on it with Invoke-Command.
The function does not know what $ip is.
You need to change the function to accept a parameter of $ip