Passing a string with a function and always getting $null variable - powershell

I am using the following in the start of a function that is meant to query a computer for various wmi objects. If just get_volumes is ran alone it should use 127.0.0.1. It seems that when I run get_volumes without any string this it passes right on to the else segment. What is the proper or a better way to accomplish this?
PS> function get_volumes([string]$a){
if ($a -eq $null){
write-host 'Using localhost'
$a = '127.0.0.1'
}else{ write-host 'Using' $a}
}
PS>get_volumes
Using
Thanks

$a is an empty string and not $null. Try this instead:
function Get-Volumes([string]$IPAddress = '127.0.0.1'){
Write-Host "Using $IPAddress"
}
Note that you can set a default value for when the user doesn't supply an argument for a parameter.

Related

PowerShell If statement not equating properly

What am I doing wrong here?
Why do the 2 variables not equal each other?
When I run this script
$temp1 = "#{Dhcp=Disabled}"
$temp2 = Get-NetIPInterface My_Ethernet | select Dhcp
write-host ""
write-host "1" $temp1
write-host "2" $temp2
write-host ""
if ($temp2 -eq $temp1){
write-host "IP address is Static "
}
Else {
write-host "IP address is Not Static"
}
I get this result
1 #{Dhcp=Disabled}
2 #{Dhcp=Disabled}
IP address is Not Static
With the helpful suggestion from Mathias this is now working as expected
$temp1 = "Disabled"
$temp2 = Get-NetIPInterface My_Ethernet | select Dhcp
write-host ""
write-host ""
write-host "1" $temp1
write-host "2" $temp2.dhcp
write-host ""
write-host ""
if ($temp2.dhcp -eq $temp1){
write-host "IP address is Static "
}
Else {
write-host "IP address is Not Static"
}
Just to complement your own effective solution:
Since your intent was to compare a property's value to another value, select -ExpandProperty Dhcp would have returned that value directly (see the docs for Select-Object):
if ((Get-NetIPInterface My_Ethernet | select -ExpandProperty Dhcp) -eq $temp1) { # ...
However, it would be much simpler to use direct property access, using ., the member-access operator:
if ((Get-NetIPInterface My_Ethernet).Dhcp -eq $temp1) { # ...
Note that .Dhcp would work even if your Get-NetIPInterface call returned multiple objects, in which case an array of values is returned, courtesy of PowerShell's member-access enumeration feature.[1]
Finally, note that Write-Host is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file. To output a value, use it by itself; e.g. $value, instead of Write-Host $value (or use Write-Output $value); see this answer.
To explicitly print only to the display but with rich formatting, use Out-Host, given that the .ToString() stringification that Write-Host uses on its input is often unhelpful - see this post.
[1] Note that PowerShell's comparison operators such as -eq exhibit filtering behavior when their LHS is an array (collection); that is, instead of returning $true or $false, they then return the sub-array of matching elements - see about_Comparison_Operators.

Powershell parameters passing in for loop to function

i'm new to powershell, i have a specific task and would like to create a port scanner in powershell that allows to pass parameters in a foreach loop to a specific parameter in the function.
The function is called testport, the specific parameter i would like to pass when running the function in a for loop is $a - which would be a list of IP's i'm interested in scanning. Here's the function:
function testport ($hostname='10.0.0.$a',$port=445,$timeout=100) {
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($hostname,$port,$requestCallback,$state)
Start-Sleep -milli $timeOut
if ($client.Connected) { $open = $true } else { $open = $false }
$client.Close()
[pscustomobject]#{hostname=$hostname;port=$port;open=$open}
the foreach loop to run the function would look like this:
foreach ($a in 1..255) {testport}
when ran, it runs against 10.0.0.$a multiple times, instead of 10.0.0.1, 10.0.0.2 etc.
How can i make this run properly?
You cant use $a in your functions $hostname parameter default value as $a doesnt exist until powershell gets to the foreach statement (after its already read your function). At best $a will already exist and have some unknown value, or it wont exist at all - either way you get the wrong output. Your on the right lines, but you need to make some adjustments...
I would advise you make your testing function generic (so it accepts a normal IP), then in your for loop put your logic to generate the IPs to scan.
So your for loop might look something like:
foreach ($a in 1..255) {
Write-Host "Testing ports on 10.0.0.$a";
testport -hostname "10.0.0.$a"
}
Then i would alter the function signature to remove $hostname's default value ('10.0.0.$a'). Your testport function declaration should look like this:
function testport ($hostname,$port=445,$timeout=100)
(NOTE: if you explicitly pass $hostname a value it will use that value instead of the default - but given the IP will always be different it probably doesnt make sense to give it a default value)

Powershell get SQL instance

I am using the below script to get the SQL instance name and then perform some tasks related to it. I am unable to get it working can you help me figure out how to fix the issue?
$SQLInstances = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances
foreach ($sql in $SQLInstances) {
[PSCustomObject]#{
InstanceName = $sql
}
}
Write-Host "The following SQL Instances were detected on the server $env:Computername $SQLInstances" -ForegroundColor Yellow
If ($SQLInstances -ne "MSSQLSERVER")
{Write-Host $($env:Computername)\$($SQLInstances)
$serverName = $($env:Computername)\$($SQLInstances)}
Else {Write-Host "Standard SQL Instance was found, proceeding with the script."
$ServerName = $env:Computername}
So the above code doesn't work, Write-Host displays"ComputerName(SPACE)\InstanceName" so there is an extra space there that i want to get rid off, plus $serverName = $($env:Computername)\$($SQLInstances)} throws " unexpected token / expression
How Do i fix this?
You are seeing this issue with Write-Host because of how the parser is interpreting tokens. () or $() (unless () or $() is part of a string) is going to be treated as one argument in expression mode. Since the default separator for Write-Host is a space, you get the extra space between each of your processed arguments. If you surround your entire value with double quotes, it will be parsed as a single token.
Write-Host "$($env:Computername)\$SQLInstances"
Regarding the $serverName assignment, it is also a parsing issue. When assigning to a variable, the parser is in expression mode. Therefore, the value must be a valid expression. The \ character is what throws the error. Double quotes will also suffice here.
$serverName = "$($env:Computername)\$SQLInstances"
Try the Get-SqlInstance cmdlet from module sqlserver:
Import-Module sqlserver
If (Get-SQLInstance -ServerInstance "MSSQLSERVER"){
# Instance found
}
Else {
# Instance not found

echo in while loop get's added to my return value

I wasn't sure how to describe this problem in the title so here goes.
I call a function from a script in another script. In that function i have a while loop that basically keeps looping through a set of ip's and looks up their hostname. when the while loop times out or we have all the host names.
it returns the hostnames.
My problem is that the return value contains every single Write-Host i'm doing in that function.
i know it's because Write-Host puts stuff on the pipeline and the return just returns whatever it has.
How do i go about fixing this?
The entire script i run get's logged in a log file which is why i want to have some verbose logging.
| out-null on write-host fixes the issue but it doesn't print the write-host values in the script.
in main.psm1 i have a function like so:
$nodes = #("ip1", "ip2", "ip3", "ip4")
$nodesnames = DoStuff -nodes $nodes
then in functions.psm1 i have functions like:
Function DoStuff
{
param($nodes)
$timeout = 300
$timetaken = 0
$sleepseconds = 5
$nodenames = #("$env:COMPUTERNAME")
while(($nodenames.count -lt $nodes.count) -and ($timetaken -lt $timeout))
{
try
{
Write-Host "Stuff"
foreach($node in $nodes)
{
$nodename = SuperawesomeFunction $node
Write-Host "$nodename"
if($nodenames -notcontains $nodename)
{
$nodenames += #($nodename)
}
}
}
catch
{
Write-Host "DoStuff Failed because $_"
}
Start-Sleep $sleepseconds
$timetaken += $sleepseconds
}
return $nodenames
}
Function SuperawesomeFunction
{
param($node)
$nodename = [System.Net.Dns]::GetHostEntry("$node")
return $nodename
}
Thanks.
So the answer is, your function is working like it is by design. In PowerShell a function will return output in general to the pipeline, unless specifically directed otherwise.
You used Echo before, which is an alias of Write-Output, and output is passed down the pipe as I mentioned before. As such it would be collected along with the returned $nodenames array.
Replacing Echo with Write-Host changes everything because Write-Host specifically tells PowerShell to send the information to the host application (usually the PowerShell Console or PowerShell ISE).
How do you avoid this? You could add a parameter specifying a path for a logfile, and have your function update the logfile directly, and only output the relevant data.
Or you can make an object with a pair of properties that gets passed back down the pipe which has the DNS results in one property, and the errors in another.
You could use Write-Error in the function, and set it up as an advanced function to support -errorvariable and capture the errors in a separate variable. To be honest, I'm not sure how to do that, I've never done it, but I'm 90% sure that it can be done.

How to distinguish between empty argument and zero-value argument in Powershell?

I want to be able to pass a single int through to a powershell script, and be able to tell when no variable is passed. My understanding was that the following should identify whether an argument is null or not:
if (!$args[0]) { Write-Host "Null" }
else { Write-Host "Not null" }
This works fine until I try to pass 0 as an int. If I use 0 as an argument, Powershell treats it as null. Whats the correct way to be able to distinguish between the argument being empty or having a zero value?
You can just test $args variable or $args.count to see how many vars are passed to the script.
Another thing $args[0] -eq $null is different from $args[0] -eq 0 and from !$args[0].
If users like me come from Google and want to know how to treat empty command line parameters, here is a possible solution:
if (!$args) { Write-Host "Null" }
This checks the $args array. If you want to check the first element of the array (i.e. the first cmdline parameter), use the solution from the OP:
if (!$args[0]) { Write-Host "Null" }
If the variable is declared in param() as an integer then its value will be '0' even if no value is specified for the argument. To prevent that you have to declare it as nullable:
param([AllowNull()][System.Nullable[int]]$Variable)
This will allow you to validate with If ($Variable -eq $null) {}