Powershell DHCP scope options extraction failure using Invoke-Command - powershell

I'm trying to extract dhcp scope option info from a list of servers, the list obtained by querying AD for authorized dhcp servers in the domain. I'm using powershell's invoke-command to pass netsh dhcp server \\$servername scope $IP show optionvalue to a remote server. The $IP variable isn't passing the way the command wants to see it. It throws the The command needs a valid Scope IP Address. error.
I'm getting the scope ip address by first running netsh dhcp server \\$servername show scope and extracting the scope ip from that output, storing it in $IP.
I can type the IP in manually into the script and it returns the scope options but passing in the variable always returns the error. I've tested the command itself in a powershell console, both by manually typing in the IP and by creating a variable with the IP (as a string) and using it in the command, which works as well. There are no special characters, that i can tell, or white spaces when i store the IP in the script. I trim those out. I've also tried converting the string to an IP address using [IPAddress], to no avail.
Here is the code that gathers the scope info and then attempts to get the scope options:
foreach ($n in $name) {
$n
$showScopes = Invoke-command -computername $n -ScriptBlock {netsh dhcp server \\$n show scope}
$formatScopeInfo = $showScopes | ? {$_.Trim() -ne "" -and ($_.Contains("Disabled") -or $_.Contains("Active"))}
foreach ($en in $formatScopeInfo) {
$scopeIps = $en.Split("-")
$IP = [IPAddress]$scopeIps[0].Trim()
$IP.IPAddressToString
Invoke-Command -ComputerName $n -ScriptBlock {netsh dhcp server \\$n scope $IP.IPAddressToString show optionvalue}
}
The first foreach works and removes the lines that don't contain scope info. The second foreach partially works, it does strip out the IP. Initially i just stored it as a string, $IP = $scopeIps[0].Trim() but that wasn't working. I tried a number of things. I tried converting the octets to integers and joining them with ".", I tried to store the whole command as as a string and pass that into the Invoke-Command. Like so:
$command = "netsh dhcp server \\$n scope $IP show optionvalue"
Invoke-Command -ComputerName $n -ScriptBlock {$command}
The ultimate goal is to be able to extract any configured scope options, wherever they may be configured (server, reservation...etc). I fear I've gotten to that point where I'm so hyper-focused on what I think is the problem, that I may be missing something simple and/or crucial elsewhere. My opinion is that the command wants to see an actual IP address but my attempts to pass the variable that way have failed (and it works in the powershell console when saved as a string).
Fair disclosure, I'm still very much a novice and I was reluctant to post my code. I see so many on here with incredibly elegant solutions to things and, by comparison, my stuff seems extremely clunky. I've never had to post before as most times I can find/figure out where i've gone wrong. But I endeavor to learn and I have spent the better part of this weekend googling with no results. I have seen the script out there that works for pre 2012 servers but I really enjoy writing my own. I'm not looking to have anyone "do it for me", if you can point me down the appropriate rabbit hole; I'm happy to venture down it. Any suggestions on the code itself (appearance, better way of doing something...etc) are appreciated as well.
Apologies for the verbosity. I'm stuck and appreciate any help.

In your invoke-command you are not passing parameter,it should be like this:
$showScopes = Invoke-command -computername $n -ScriptBlock {
param($n)
netsh dhcp server \\$n show scope
} -argumentlist $n
and
Invoke-Command -ComputerName $n -ScriptBlock {
param($n,$IP)
netsh dhcp server \\$n scope $IP.IPAddressToString show optionvalue
} -argumentlist $n,$IP

Related

Powershell Test-NetConnection returns False in loop script

Test-NetConnection returns TRUE when run manually but when in a looping script, only some of the ports returns TRUE.
I wrote a powershell script that loops through port numbers to do a Test-NetConnection:
$machine = '[targetmachinename]'
$this_machine = $env:COMPUTERNAME
$port_arr = #(8331, 8332, 8333, 8334, 8335, 8310, 8311)
foreach ($port in $port_arr) {
Test-NetConnection $machine.domain.name.com -port $port -InformationLevel Quiet
}
When I run the script, it always returns TRUE on the same two port numbers and returns FALSE on the other ports.
When I manually run the code for each port, they each come back as TRUE for all ports.
I have tried messing around with the port numbers by removing, adding, and moving them around but it always gives the same results with only the same two port numbers returning TRUE.
I suspected maybe the variable, array, foreach loop or something might be bad, but if that was the case, why would it work for the same two ports and not for the others even when I change up the array?
I was thinking about putting a delay or wait in between loops but have not tested it yet.
This script works fine when run locally from the target machine. Having this issue when running the script from another machine.
UPDATE:
Looking at the powershell log:
Command start time: 20191111121539
**********************
PS>TerminatingError(New-Object): "Exception calling ".ctor" with "2" argument(s): "No connection could be made because the target machine actively refused it [IPADDRESS]:[PORT]""
I noticed that the IPADDRESS does not match up with the target machine name, but instead matches up with the source machine.
I replaced the $machine.domain.name.com to the actual ip address of the machine and that got the script working as expected.
Why does $machine.domain.name.com resolve to the source machine? Even if I concatenate that incorrectly, wouldn't that normally become an unresolved address and error? Shouldn't all port checks have failed at that point?
tl;dr
Replace argument
$machine.domain.name.com
with
"$machine.domain.name.com"
While unquoted command arguments in PowerShell are typically treated as expandable strings - i.e., as if they were implicitly enclosed in "...", this is not the case if your argument starts with a variable reference such as $machine.
In that case, PowerShell tries to evaluate the argument as an expression, and since [string] variable $machine has no .domain property (and subsequent nested properties), the entire argument effectively evaluates to $null[1] - resulting in inadvertent targeting of the local machine by Test-NetConnection.
The subtleties around how PowerShell parses unquoted command arguments:
are explored in this answer.
what the design rationale behind these subtleties may be is the subject of this GitHub issue.
Conversely, to learn about how expandable strings (string interpolation) - variable references and expressions embedded in "..." - work in PowerShell,
see this answer.
Additionally, BACON observes the following regarding the use of -InformationLevel Quiet with Test-NetConnection:
I think passing -InformationLevel Quiet was actively impairing debugging in this case. Given $machine = 'foo', compare the output (particularly the ComputerName property) of:
Test-NetConnection $machine.domain.name.com -InformationLevel Quiet
vs.
Test-NetConnection $machine.domain.name.com
vs.
Test-NetConnection "$machine.domain.name.com".
In other words, [it's best to] ensure that the cmdlet (and its parameters) is behaving as expected before passing the parameter that says "I don't care about all that information. Just tell me if it passed or failed."
[1] $null is the effective result by default or if Set-StrictMode -Version 1 is in effect; with Set-StrictMode -Version 2 or higher, you would actually get an error.
A common mistake I've seen people make (myself included) is in your variable name and usage in powershell. For example I forgot $ all the time. This is just looping through my machine as an example, but it tests all these ports correctly.
$port_arr = #(139,3389,5040)
$mac = #("myComputer")
foreach ($mc in $mac){
foreach ($i in $port_arr) {
Test-NetConnection $mc -port $i
}
}
Do you have an example of your powershell code? Also, have you stepped through to determine that it's working as expected?

Bulk IP configuration using CSV input

I have a CSV file with the following values for each machine I want to remotely reconfigure using static IP
name,nic,ip,mask,defaultgw
I was hoping to be able to reconfigure the IPs for each listed but if I have more than one machine listed the script gets stuck. This is because at the end of the first loop iteration, unless I manually do an ipconfig /flushdns on the server the script is running from, I will lose connection to the server being configured and the script just hangs leaving the rest of the servers. What I have so far is this:
$csv = import-csv "c:\scripts\builds\machines.csv"
foreach ($Row in $csv) {
$machine = $Row.name
$Nic = $row.Nic
$address = $row.IP
$mask =$row.mask
$defaultgw = $row.gw
invoke-command -computername $machine -scriptblock { Get-NetIpAddress - InterfaceAlias $using:nic | New-NetIPAddress -ipaddress $using:address -PrefixLength $using:mask -DefaultGateway $using:defaultgw | Register-DnsClient}}
}
Can this be done using workflows or just simple start-job?
I suspect you're losing the connection to the remote machine once you change the IP address, while the local machine hangs trying to keep the connection.
Try making the call, and dropping off a payload, then running it after you disconnect.
That is, upload the code, then spawn it, then disconnect before it has a chance to run (add a sleep to the remote code maybe?). This way, you can launch your payload and disconnect before it affects you.
For example, you could copy a file to that machine with the values it needs, then schedule a task to run in 5 seconds in the future, then disconnect before it runs. The task will then run, and since you're already disconnected, you won't be affected by it.
You might also consider DHCP with static reservations. It's far easier to manage than what you're trying to do here.

using powershell to check active IP's or MAC's connected to router

I want to run a Powershell script that talks to the router/AP and figures out what IP (I have Reserved IPS) or MAC address is currently connected to the Router/AP. The script would output what is connected so that I could see "who's home".
At first I used IE though powershell logging into the router and trying to capture data of the wifi client page but I don't think that is the way to go. Is there another way to do this? A way to scan the network without worrying about logging into the router?
If you have DNS resolution on the names of your PCs, you can try this out.
$Computer = "value or foreach loop of values"
$IPAddress = ([System.Net.Dns]::GetHostByName($Computer).AddressList[0]).IpAddressToString
$IPMAC = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $Computer
$MACAddress = ($IPMAC | where { $_.IpAddress -eq $IPAddress}).MACAddress
I have tested this with a couple individual names and a foreach loop getting the names from a txt or csv file, and I tested using
write-host $IPAddress $MACAddress
at the end for a sanity check.
If you want to verify an up/down state of the computer before querying, try using the 'test-connection' powershell command (basically a ping that grabs the results)

Invalid computer name error with remote execution of PowerShell script

I am trying to execute some PowerShell code in a remote computer using the following:
$session = New-PSSession -Credential "myDomain\myUserName" -ComputerName "remoteCompName"
$result = Invoke-Command -Session $session -ScriptBlock {
New-Item -type file C:\test10.txt
}
I am prompted to enter my password in a GUI. I do that. It then errors out with:
New-PSSession : 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.
I replaced the computer name with the FQDN. Still no luck. What is going on here?
There are other questions on stackoverflow on executing PowerShell scripts on remote machines of course but none address this error.
BTW, the machine is part of the domain and is running.
Additional info added later [EDIT]
Things to know:
The machine is part of the domain and is running.
I checked if PS remoting is enabled. It was.
I checked if WinRM is running. It is.
The remote machine is a VM and it is a 2012 R2.
Here is what I've tried:
I replaced the computer name with the FQDN. Still no luck.
I removed the credential parameter
I tried another remote machine (also a VM)
I tried another source machine, i.e. the machine I am running the command from)
Thanks!
-Rohan.
When I use a remote machine with a name that is just alphanumeric (no underscores, dashes, etc.), it works! The name of all machines I tried before had leading '_' in them. That was the cause of the error. (The answer was suggested by Rhys W Edwards on the Windows PowerShell TechNet forum, which is within Windows Server forums).
Just put your ComputerName to braces like this {Remote_Computer_Name}

Powershell Remoting Speeding up a Foreach Loop Hosted Exchange

I have a CSV of email adddresses and Departments that I need to set on Live#edu. The command I currently have looks something like this:
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department }`
The problem is, this operation takes FOREVER.
My first thought is that it would be great to have the ForEach-Object command actually be forwarded over to the remote machine, so that it will only need to create the one pipeline between the two machines, but when I go into the PSSession, there doesn't seem to be any foreach-object available. For reference, How I Import the PSSession is:
Import-PSSession(New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri 'https://ps.outlook.com/powershell' `
-Credential (Get-Credential) `
-Authentication Basic -AllowRedirection)
Is there a better way that I can import the session to allow ForEach-Object to be remote, or to import an aliased version of the remote foreach-object, perhaps as ForEach-Object-Remote, or perhaps does anybody have something better to suggest to streamline this process?
UPDATE:
A Couple Things I've tried:
Using the -AsJob switch on the implicitly remoted command.
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department -AsJob }
This, unfortunately, doesn't work because there are throttling limits in place that don't allow the additional connections. Worse than that, I don't even know that anything went wrong until I check the results, and find that very few of them actually got changed.
Importing the ForEach-Object under a different name.
Turns out that adding a prefix is easy as putting -Prefix RS in the Import-PSSession Command to have things like the ForEach-Object from the Remote Session become ForEach-RSObject in the local session. Unfortunately, this won't work for me, because the server I'm connecting to does not does not have the Microsoft.Powershell ConfigurationName available to me.
UPDATE 2: The Set-User cmdlet seems to be Microsoft provided for Live#edu administration. Its purpose is to set User attributes. It is not a script or cmdlet that I am able to debug. It doesn't take pipeline input, unfortunately, so that would not be able to fix the issue.
As Far as I can tell, the problem is that it has to construct and tear down a pipeline to the remote machine every time this command runs, rather than being able to reuse it. The remote ForEach idea would have allowed me to offload that loop to avoid having to create all those remote pipelines, while the -asJob would have allowed them to all run in parallel. However, it also caused errors to fail silently, and only a few of the records actually get properly updated.
I suspect at this point that I will not be able to speed up this command, but will have to do whatever I can to limit the amount of data that needs to be changed in a particular run by keeping better track of what I have done before (keeping differential snapshots). Makes the job a bit harder.
EDIT: Start-Automate left a very useful help, unfortunately, neither of them work. It is my feeling at this point that I won't find a way to speed this up until my provider gives access to more powershell cmdlets, or the exchange cmdlets are modified to allow multiple pipelines, neither of which I expect to happen any time soon. I am marking his answer as correct, despite the ultimate result that nothing helps significantly. Thanks, Start-Automate.
You can speed up your script and also avoid trying to make two connections to the server by the use of the foreach statement, instead of Foreach-Object.
$departments = #(import-csv .\departments.csv)
foreach ($user in $departments) {
Set-User $user.EmailAddress $user.Department
}
If you need to batch, you could use the for statement, moving forward in each batch
for ($i =0; $i -lt $departments.Count; $i+=3) {
$jobs = #()
$jobs+= Invoke-Command { Set-User $departments[$i].EmailAddress $departments[$i].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 1].EmailAddress $departments[$i + 1].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 2].EmailAddress $departments[$i + 2].Department } -AsJob
$jobs | Wait-job | Receive-job
}
Hope this helps