I am creating some tools for active directory management and have run into an issue where IP Addresses are not being converted to hostnames when searching AD through powershell. As a workaround I created this Resolve-Hostname function and if I run it by itself it works just fine. As soon as I put it in my overall script and try calling upon it, it does not convert the IP to a Hostname...
Below is the function I created
function Resolve-Hostname {
If ($Workstation -like "*.*.*.*") #Convert IP Address to Hostname
{
$NameHost = resolve-dnsname $Workstation | Select-Object -ExpandProperty NameHost -First 1
$Workstation = ($NameHost -Split {$_ -eq "."})[0]
}
} #close of create the Resolve-Hostname function
Anyone that has any idea how to implement this better feel free to chip in as well, I am by no means an expert.
I am not sure where my issue lies so I will start from the top, any recommendations for optimizing my script would be greatly appreciated.
Sets the size of the window
$pshost = get-host
$pswindow = $pshost.ui.rawui
$newsize = $pswindow.windowsize
$newsize.height= 30
$newsize.width = 60
$pswindow.windowsize = $newsize
Declares the Show-Menu and resolve-host function
#Create functions
function Show-Menu { #Create the Show-Menu function
param ([string]$Title = 'Admin Tools Menu') #Sets title
Clear-Host
Write-Host " "
Write-Host " "
Write-Host "`t$Title" -foregroundcolor Blue #Display title of menu
Write-Host "`t1: Add Admin."
Write-Host "`t2: Boot Time."
Write-Host "`t3: User 'Shr_' Groups."
Write-Host "`t4: Computer Management."
Write-Host "`t5: Active Directory."
Write-Host "`t6: Remove Admin."
Write-Host "`t7: Disable Workstation in AD"
Write-Host
Write-Host "`tQ: Enter 'Q' to quit."
Write-Host
Write-Host
} #close of create show menu function
function Resolve-Hostname {
If ($Workstation -like "*.*.*.*") #Convert IP Address to Hostname
{
$NameHost = resolve-dnsname $Workstation | Select-Object -ExpandProperty NameHost -First 1
$Workstation = ($NameHost -Split {$_ -eq "."})[0]
}
} #close of create the Resolve-Hostname function
#End of create functions
Implementation of the script below
#Begin Main Menu
do
{
Show-Menu #Displays created menu above
$Selection = $(Write-Host "`tMake your selection: " -foregroundcolor Red -nonewline; Read-Host)
switch ($selection)
{
'1' { #Add Admin
Clear-Host
$Workstation = $(Write-Host "Workstation\IP Address" -nonewline -foregroundcolor DarkGreen) $(Write-Host "?: " -NoNewline; Read-Host) #Declare Workstation/IP Address
Resolve-Hostname
$Workstation
pause
After the pause the script goes through adding the workstation to the admin group but I am fairly certain that portion is not relevant to the problem at hand. There are no errors that pop up, just that the $workstation outputs the IP address instead of resolving into the Hostname as per the resolve-hostname function.
CodePudding user response:
You cannot directly modify a variable that lives in the script scope from inside a function called from that scope, because it runs in a child scope.
- While the child scope can read the parent scope's variables (and its parent's, and so on), writing to them by name only - perhaps surprisingly - creates a function-local variable of the same name - see this answer for more information, and footnote [1] below for a demonstration.
While using the
$script:
scope modifier -$script:Workstation
, in your case - would work,[1] or, more generally,Set-Variable
-Scope 1 WorkStation ...
, accessing variables across scope boundaries is best avoided, in the interest of encapsulation.- Instead, make your function output (return) the modified value and let the caller (re)assign the output to a variable in its scope.
- Similarly, it's better for functions to receive input values as arguments, via declared parameters.
- Both techniques are shown below.
Redefine your function as follows, to make it (a) accept the value to resolve via a parameter, and (b) output the (potentially) resolved value:
function Resolve-Hostname {
param(
[string] $Workstation # Declare a parameter.
)
# Note that $Workstation now refers to the (always local) *parameter* variable.
If ($Workstation -like "*.*.*.*") { #Convert IP Address to Hostname
$NameHost = Resolve-DnsName $Workstation | Select-Object -ExpandProperty NameHost -First 1
# Extract the first "."-based component *and output it*.
($NameHost -split '\.')[0]
} else {
$Workstation # Already a hostname, output it as-is.
}
}
You'd then invoke your function as follows:
# Sample value.
$workstation = '142.251.40.238'
# Call the function with the sample value, and capture the
# output in the same local variable.
$workstation = Resolve-HostName $workstation
[1] A quick demonstration of the original problem and the $script:
workaround, using a script block ({ ... }
) in lieu of a function, invoked with &
, the call operator, which runs the block in a child scope:
$i = 42; & { $i = $i 1 }; $i
outputs 42
, because the $i = ...
assignment implicitly created a local $i
variable, so the original $i
variable in the parent scope is left untouched.
By contrast, $i = 42; & { $script:i = $i 1 }; $i
outputs 43
, due to $script:i = ...
explicitly targeting the script (parent) scope's $i
variable.