Author Posts

July 24, 2018 at 6:44 pm

What?!! Can someone explain this behavior, please?

PS C:\Users\acutech> $MY_MAC="08-00-27-35-AE-2C"
PS C:\Users\acutech> $MY_MAC.replace('-','%')
08%00%27%35%AE%2C
PS C:\Users\acutech> $MY_MAC.replace('%',':')
08-00-27-35-AE-2C < —- HERE'S THE FIRST WEIRD RESULT
PS C:\Users\acutech> $MY_MAC.replace('-',':')
08:00:27:35:AE:2C
PS C:\Users\acutech> $MY_MAC.replace(':','-')
08-00-27-35-AE-2C
PS C:\Users\acutech> $MY_MAC = Get-WMIObject win32_networkadapterconfiguration | foreach { $_.MacAddress }
PS C:\Users\acutech> $MY_MAC
08:00:27:35:AE:2C
PS C:\Users\acutech> $MY_MAC.replace(':','-')
You cannot call a method on a null-valued expression. < —- AND THE SECOND. WHY DOES IT WORK WITH MANUAL ENTRY?
At line:1 char:1
+ $MY_MAC.replace(':','-')
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

July 24, 2018 at 6:52 pm

Replace() produces a new string, which you did not capture in a variable. It does not modify the original string.

Most methods work that way.

July 24, 2018 at 6:54 pm

Oh, and on the latter one I don't think you have a string. Try piping the variable to Get-Member to verify the type of object you've got.

July 24, 2018 at 7:03 pm

I appreciate, but do not understand, your response.

In my example, I'm just displaying the response. If I actually try to capture to a new variable:

PS C:\Users\acutech> $MY_MAC
08:00:27:35:AE:2C

PS C:\Users\acutech> $VAR1 = $MY_MAC < —- Straight copy of variable works fine
PS C:\Users\acutech> $VAR1
08:00:27:35:AE:2C

PS C:\Users\acutech> $VAR2 = $MY_MAC.Replace(':','-') < —- But trying to use Replace() does not
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $VAR2 = $MY_MAC.Replace(':','-')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

PS C:\Users\acutech> $VAR2 = $VAR1.Replace(':','-') < —- Same if I try using the new var
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $VAR2 = $VAR1.Replace(':','-')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

July 24, 2018 at 7:05 pm

And, if I manually set the variable instead of getting it from WMIObject win32_networkadapterconfiguration, it works fine:

PS C:\Users\acutech> $MY_MAC = "08:00:27:35:AE:2C"
PS C:\Users\acutech> $MY_MAC.Replace(':','-')
08-00-27-35-AE-2C

July 24, 2018 at 7:07 pm

Oh, wait. I get what you're saying now. You're addressing the first weird result. And now I see that. Duh! How dumb of me.

Okay, the first weird result is not weird at all. I just wasn't thinking straight. It's still working on the original string, because I didn't replace it.

Got it! Thanks!

July 24, 2018 at 7:08 pm

How about this....

foreach($mac in ($Macs = Get-WmiObject win32_networkadapterconfiguration | ? { $_.macaddress -ne $null } | select description, macaddress ) )
{
    $($mac.macaddress)
}

July 24, 2018 at 7:09 pm

But that still leaves the second weird result. Why does the replace work when I manually set the variable, but not when it gets set by the Get-NetAdapter command, which appears to me to produce the exact same value?

Thanks!

July 24, 2018 at 7:11 pm

Because regardless of appearances what NetAdapter is giving you is not a String object. It doesn't have a Replace() method.

You wanna code in .NET, you gotta follow its rules. And know that PowerShell can make those fuzzy and confusing.

Again, pipe your result to GM. Or use -replace.

July 24, 2018 at 7:14 pm


foreach($mac in ($Macs = Get-WmiObject win32_networkadapterconfiguration | ? { $_.macaddress -ne $null } | select description, macaddress ) )
{
    $($mac.macaddress) -replace ":","-"
}


July 24, 2018 at 7:30 pm

Okay, now I think I'm getting it.

Get-NetAdapter and WMIObject win32_networkadapterconfiguration don't produce strings, and .Replace() is a string-type function.

Also, from what you say, I understand these to be .NET commands, not Powershell commands. (I am almost completely unfamiliar with both, only using Powershell because batch is totally underpowered and traditional tools (Unix shells, tools like grep and awk and curl and wget and everything else I need but am having to figure out in Powershell by digging and scraping for hours and days) are unavailable (natively) on Windows. (And to make matters worse, this is in a PXE environment which ships a crippled Powershell, not having such basics as Get-NetAdapter and Write-Out. Pfft.)

I had read that everything in Powershell is an object, but I haven't yet gotten to the point where I naturally realize that what looks like a string ain't.

So I'm guessing that your code snippet not only gets the MAC address, but also converts it,or at least part of it, into a string. When I run it, I can then display either "$Macs.macaddress" or "macs.macaddress", and get identical (to my eyes) output, and adding .Replace(':',"-') to both strings produces (to my eyes) the output I seek.

I don't understand what the question mark does, and I don't know why you're selecting "description" as well as "macaddress" (but I don't figure it hurts anything), and I don't know where the conversion to a string is taking place (why this snippet produces a different result than my original code), but I do understand that when I manually set my variable, I'm setting a string variable, and when I use Get-NetAdapter (or its friends), I'm getting an object, and that's why the string-based function ("method", in OO parlance, I believe) works on the manual setting but not the Get- setting.

I understand more than I did, and that's a good thing. Thank you very much!

July 24, 2018 at 7:36 pm

Okay, maybe I'm not totally getting it after all. It looks to me that whether I set the variable manually, or use the WMI call, the "Get-Member" shows them to be identical, and both have the Replace method. Yet Replace doesn't work on both.

PS C:\Users\acutech> $MY_MAC = "08:00:27:35:AE:2C"
PS C:\Users\acutech> $MY_MAC | Get-Member

TypeName: System.String

Name MemberType Definition
—- ———- ———-
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB), int IComparab...
Contains Method bool Contains(string value)
CopyTo Method void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co...
EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari...
Equals Method bool Equals(System.Object obj), bool Equals(string value), bool Equals(string...
GetEnumerator Method System.CharEnumerator GetEnumerator(), System.Collections.IEnumerator IEnumer...
GetHashCode Method int GetHashCode()
GetType Method type GetType()
GetTypeCode Method System.TypeCode GetTypeCode(), System.TypeCode IConvertible.GetTypeCode()
IndexOf Method int IndexOf(char value), int IndexOf(char value, int startIndex), int IndexOf...
IndexOfAny Method int IndexOfAny(char[] anyOf), int IndexOfAny(char[] anyOf, int startIndex), i...
Insert Method string Insert(int startIndex, string value)
IsNormalized Method bool IsNormalized(), bool IsNormalized(System.Text.NormalizationForm normaliz...
LastIndexOf Method int LastIndexOf(char value), int LastIndexOf(char value, int startIndex), int...
LastIndexOfAny Method int LastIndexOfAny(char[] anyOf), int LastIndexOfAny(char[] anyOf, int startI...
Normalize Method string Normalize(), string Normalize(System.Text.NormalizationForm normalizat...
PadLeft Method string PadLeft(int totalWidth), string PadLeft(int totalWidth, char paddingChar)
PadRight Method string PadRight(int totalWidth), string PadRight(int totalWidth, char padding...
Remove Method string Remove(int startIndex, int count), string Remove(int startIndex)
Replace Method string Replace(char oldChar, char newChar), string Replace(string oldValue, s...
Split Method string[] Split(Params char[] separator), string[] Split(char[] separator, int...
StartsWith Method bool StartsWith(string value), bool StartsWith(string value, System.StringCom...
Substring Method string Substring(int startIndex), string Substring(int startIndex, int length)
ToBoolean Method bool IConvertible.ToBoolean(System.IFormatProvider provider)
ToByte Method byte IConvertible.ToByte(System.IFormatProvider provider)
ToChar Method char IConvertible.ToChar(System.IFormatProvider provider)
ToCharArray Method char[] ToCharArray(), char[] ToCharArray(int startIndex, int length)
ToDateTime Method datetime IConvertible.ToDateTime(System.IFormatProvider provider)
ToDecimal Method decimal IConvertible.ToDecimal(System.IFormatProvider provider)
ToDouble Method double IConvertible.ToDouble(System.IFormatProvider provider)
ToInt16 Method int16 IConvertible.ToInt16(System.IFormatProvider provider)
ToInt32 Method int IConvertible.ToInt32(System.IFormatProvider provider)
ToInt64 Method long IConvertible.ToInt64(System.IFormatProvider provider)
ToLower Method string ToLower(), string ToLower(cultureinfo culture)
ToLowerInvariant Method string ToLowerInvariant()
ToSByte Method sbyte IConvertible.ToSByte(System.IFormatProvider provider)
ToSingle Method float IConvertible.ToSingle(System.IFormatProvider provider)
ToString Method string ToString(), string ToString(System.IFormatProvider provider), string I...
ToType Method System.Object IConvertible.ToType(type conversionType, System.IFormatProvider...
ToUInt16 Method uint16 IConvertible.ToUInt16(System.IFormatProvider provider)
ToUInt32 Method uint32 IConvertible.ToUInt32(System.IFormatProvider provider)
ToUInt64 Method uint64 IConvertible.ToUInt64(System.IFormatProvider provider)
ToUpper Method string ToUpper(), string ToUpper(cultureinfo culture)
ToUpperInvariant Method string ToUpperInvariant()
Trim Method string Trim(Params char[] trimChars), string Trim()
TrimEnd Method string TrimEnd(Params char[] trimChars)
TrimStart Method string TrimStart(Params char[] trimChars)
Chars ParameterizedProperty char Chars(int index) {get;}
Length Property int Length {get;}

PS C:\Users\acutech> $MY_MAC.Replace(':','-')
08-00-27-35-AE-2C

PS C:\Users\acutech> $MY_MAC = Get-WMIObject win32_networkadapterconfiguration | foreach { $_.MacAddress }
PS C:\Users\acutech> $MY_MAC | Get-Member

TypeName: System.String

Name MemberType Definition
—- ———- ———-
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB), int IComparab...
Contains Method bool Contains(string value)
CopyTo Method void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co...
EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari...
Equals Method bool Equals(System.Object obj), bool Equals(string value), bool Equals(string...
GetEnumerator Method System.CharEnumerator GetEnumerator(), System.Collections.IEnumerator IEnumer...
GetHashCode Method int GetHashCode()
GetType Method type GetType()
GetTypeCode Method System.TypeCode GetTypeCode(), System.TypeCode IConvertible.GetTypeCode()
IndexOf Method int IndexOf(char value), int IndexOf(char value, int startIndex), int IndexOf...
IndexOfAny Method int IndexOfAny(char[] anyOf), int IndexOfAny(char[] anyOf, int startIndex), i...
Insert Method string Insert(int startIndex, string value)
IsNormalized Method bool IsNormalized(), bool IsNormalized(System.Text.NormalizationForm normaliz...
LastIndexOf Method int LastIndexOf(char value), int LastIndexOf(char value, int startIndex), int...
LastIndexOfAny Method int LastIndexOfAny(char[] anyOf), int LastIndexOfAny(char[] anyOf, int startI...
Normalize Method string Normalize(), string Normalize(System.Text.NormalizationForm normalizat...
PadLeft Method string PadLeft(int totalWidth), string PadLeft(int totalWidth, char paddingChar)
PadRight Method string PadRight(int totalWidth), string PadRight(int totalWidth, char padding...
Remove Method string Remove(int startIndex, int count), string Remove(int startIndex)
Replace Method string Replace(char oldChar, char newChar), string Replace(string oldValue, s...
Split Method string[] Split(Params char[] separator), string[] Split(char[] separator, int...
StartsWith Method bool StartsWith(string value), bool StartsWith(string value, System.StringCom...
Substring Method string Substring(int startIndex), string Substring(int startIndex, int length)
ToBoolean Method bool IConvertible.ToBoolean(System.IFormatProvider provider)
ToByte Method byte IConvertible.ToByte(System.IFormatProvider provider)
ToChar Method char IConvertible.ToChar(System.IFormatProvider provider)
ToCharArray Method char[] ToCharArray(), char[] ToCharArray(int startIndex, int length)
ToDateTime Method datetime IConvertible.ToDateTime(System.IFormatProvider provider)
ToDecimal Method decimal IConvertible.ToDecimal(System.IFormatProvider provider)
ToDouble Method double IConvertible.ToDouble(System.IFormatProvider provider)
ToInt16 Method int16 IConvertible.ToInt16(System.IFormatProvider provider)
ToInt32 Method int IConvertible.ToInt32(System.IFormatProvider provider)
ToInt64 Method long IConvertible.ToInt64(System.IFormatProvider provider)
ToLower Method string ToLower(), string ToLower(cultureinfo culture)
ToLowerInvariant Method string ToLowerInvariant()
ToSByte Method sbyte IConvertible.ToSByte(System.IFormatProvider provider)
ToSingle Method float IConvertible.ToSingle(System.IFormatProvider provider)
ToString Method string ToString(), string ToString(System.IFormatProvider provider), string I...
ToType Method System.Object IConvertible.ToType(type conversionType, System.IFormatProvider...
ToUInt16 Method uint16 IConvertible.ToUInt16(System.IFormatProvider provider)
ToUInt32 Method uint32 IConvertible.ToUInt32(System.IFormatProvider provider)
ToUInt64 Method uint64 IConvertible.ToUInt64(System.IFormatProvider provider)
ToUpper Method string ToUpper(), string ToUpper(cultureinfo culture)
ToUpperInvariant Method string ToUpperInvariant()
Trim Method string Trim(Params char[] trimChars), string Trim()
TrimEnd Method string TrimEnd(Params char[] trimChars)
TrimStart Method string TrimStart(Params char[] trimChars)
Chars ParameterizedProperty char Chars(int index) {get;}
Length Property int Length {get;}

PS C:\Users\acutech> $MY_MAC = Get-WMIObject win32_networkadapterconfiguration | foreach { $_.MacAddress }
PS C:\Users\acutech> $MY_MAC.Replace(':','-')
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $MY_MAC.Replace(':','-')
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

July 24, 2018 at 7:39 pm

But, at this point, I don't need to understand it further; I understand enough to know that it simply doesn't work the way I would expect it to, and I need to find another way. I'll probably use your code snippet. Thank you for providing it.

July 24, 2018 at 9:02 pm

In playing with this some more, I find that when I run:

$MY_MAC = Get-WmiObject win32_networkadapterconfiguration

I can then check for the properties:

$MY_MAC | Get-Member

and see that .MACAddress is one of those properties. When I check the value of that property:

$MY_MAC.MACAddress

I get what looks like what I expect:

08:00:27:35:AE:2C

but apparently not exactly, because when I check its length:

PS C:\Users\acutech> $MY_MAC.MACAddress.Length
2

I get "2" instead of the expected "17", which is what I get when I set the variable manually.

Aha! Perhaps this is an array, with one of the elements holding the string I seek. And sure enough:

PS C:\Users\acutech> $MY_MAC.MACAddress[1]
08:00:27:35:AE:2C
PS C:\Users\acutech> $MY_MAC.MACAddress[1].Length
17

And best of all, the Replace() method works on this array element:

PS C:\Users\acutech> $MY_MAC.MACAddress[1].Replace(':','-')
08-00-27-35-AE-2C

So here's my new code (verbose, for readability/understandability; I'm sure it could be made more efficient):

PS C:\Users\acutech> $MY_MAC = Get-WmiObject win32_networkadapterconfiguration
PS C:\Users\acutech> $MY_MAC = $MY_MAC.MACAddress[1]
PS C:\Users\acutech> $MY_MAC = $MY_MAC.Replace(':','-')
PS C:\Users\acutech> $MY_MAC
08-00-27-35-AE-2C

July 25, 2018 at 12:39 pm

Nicely sleuthed! As you've seen, there is often a few ways to do something, and what you see isn't always accurate at first glance. Part of the reason for this will be that in order to display the objects, PowerShell will (often) do one of two things:
1. Look for a relevant format file for the type. These typically contain a list of properties of the object to show, and information indicating how to display the information (usually with Format-Table or Format-List).
2. It tries to list the object's properties and values, if possible.
3. If no format file is available and #2 fails for some reason, PoSH will call the object's .ToString() method in order to get it to display something. This may not be a particularly useful piece of information — many complex objects simply return their type name when asked to turn into a string.

A possible alternate method for you in this specific case:

$MY_MAC = Get-WmiObject -ClassName 'win32_networkadapterconfiguration' |
    Select-Object -Property MacAddress -Last 1
$MY_MAC = $MY_MAC.Replace(':','-')
$MY_MAC

July 25, 2018 at 1:14 pm

Using indexes isn't really ideal, especially when you start talking to multiple systems that have different network adapter configurations, there is no guarantee that the index will be the same . It's better to find the object using a filter, so I typically start with something like this to see all of the available properties and values:

Get-WmiObject -ClassName 'win32_networkadapterconfiguration' -Property * | Select *

Then once you find the properties you can filter on, you can return only the objects you want. Additionally, you have leverage calculated properties to return the MAC address formatted like you want:

$mac = Get-WmiObject -ClassName 'win32_networkadapterconfiguration' -Filter "(IPEnabled = $true) and (DHCPEnabled = $true)" |
       Select PSComputerName,
              Index,
              Description,
              MacAddress,
              @{Name="MoBettaMac";Expression={$_.MacAddress.Replace(":","-")}}

$mac

July 25, 2018 at 2:28 pm

1 line.....

@(Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "(IPEnabled = $true) and (DHCPEnabled = $true)" | Select PSComputerName, @{L='IPAddress';ex={$_.IPAddress}},MacAddress, Index, Description,@{Name="MoBettaMac";Expression={$_.MacAddress.Replace(":","-")}}) 

July 25, 2018 at 4:27 pm

You can do it like this:

PS C:\users\me> $MY_MAC="08-00-27-35-AE-2C"
PS C:\users\me> $MY_MAC
08-00-27-35-AE-2C
PS C:\users\me> $MY_MAC = $MY_MAC.replace('-','%')
PS C:\users\me> $MY_MAC
08%00%27%35%AE%2C
PS C:\users\me> $mac = get-netadapter | select -expand macaddress
PS C:\users\me> $mac = $mac.replace('-','%')

July 25, 2018 at 7:32 pm

Ah, Rob Simmer's suggestion of

Get-WmiObject -ClassName 'win32_networkadapterconfiguration' -Property * | Select *

led me to additional understanding. The reason the .MACAddress property is an array is because there's an array element for each network adapter on the system. And his suggestion that this array may not always be structured the same makes sense, because some machines will have two or three NICS, plus a wireless adapter, plus ....

In my case, the first element of the .MACAddress array is for the MAC address of the "Microsoft Kernel Debug Network Adapter", and the second is for the MAC address of the "Intel(R) PRO/1000 MT Desktop Adapter". Rob makes much sense to not rely on the array element being consistent across different machines. I wouldn't have understood this had he not suggested his command above, and had I not done it for myself to see the results on my machine.

The first part of his more-detailed example:

Get-WmiObject -ClassName 'win32_networkadapterconfiguration' -Filter "(IPEnabled = $true) and (DHCPEnabled = $true)"

is understood by me to return "live" network interfaces (one's currently having DHCP and IP enabled – I assume I could leave off the DHCP part to get both DHCP- and statically-assigned NICs?).

I understand the part where that information is piped through a "Select"ion filter, to get the PSComputerName, Index, Description, and MacAddress.

I understand the tail-end of that last line to be replacing the colons with dashes in the MacAddress property of the current ($_) item, but I don't understand the rest of the line. I believe it's referring to an array, presumably the one we've been talking about, MACAddress, but I don't suss what it's doing.

July 25, 2018 at 7:37 pm

I like the solution by js very much, but unfortunately, I can not use it, as Microsoft, in all its wisdom, has failed to include the Get-NatAdapter in the PXE version of Powershell. (There may be a way for me to add it, but I'm already barely keeping my nose above water in this world of making Windows do what I need it to do.)

July 25, 2018 at 7:40 pm

How about this?

Get-WmiObject Win32_NetworkAdapter | select -expand macaddress

July 25, 2018 at 7:42 pm

Ah, but using Get-WmiObject Win32_NetworkAdapter (as js suggests after I discovered it for myself) works!

$mac = Get-WmiObject Win32_NetworkAdapterConfiguration | select -expand macaddress
$mac = $mac.replace(':','-')
$mac

Is this method portable across various models? I like this solution a lot. It's simple.

July 25, 2018 at 7:45 pm

I would think so, but you may get back more than one interface (bluetooth, wifi, ...) and would have to filter it. Something like:

$connected = 2
Get-WmiObject win32_networkadapter | where { $_.netconnectionid -and 
  $_.netconnectionstatus -eq $connected }

July 25, 2018 at 8:31 pm

btw, what does the "-expand" do (other than make it work for me)?

Thanks!

July 25, 2018 at 8:57 pm

Without -expand you would get back a property that you would have to reference with $var.macaddress instead of just $var.

PS C:\users\j> get-netadapter | select macaddress

MacAddress
----------
8C-89-A5-B6-A6-99


PS C:\users\j> get-netadapter | select -expand macaddress
8C-89-A5-B6-A6-99

July 25, 2018 at 9:21 pm

Yes, but .MacAddress is an array, and I can't do .Replace() on the un-expanded version, so the -expand is not just stripping out the header, but is also converting it to a non-array format, seems to me.

I would go read up on it for myself, but I don't know if I'd need to read about "select", or "expand", or "Get-NetAdapter", or what. Googling for "powershell select -expand" is not being very productive.

July 25, 2018 at 9:37 pm

Ah, doing more research, I find that the

@{Name="MoBettaMac";Expression={$_.MacAddress.Replace(":","-")}}

bit creates a "new" property that is not part of the normal results; it's a "calculated property", consisting of a Name key and an Expression key. So we're creating a new property named "MoBettaMac", that consists of the expression "[results].MacAddress.Replace(':','-')".

Then to get the value I want, I'd use

$mac.MoBettaMac

I don't need to use the .Replace method on it, because that's already been done in the calculation, and the reason it worked in the calculation is because the .MacAddress property in this context is not an array, because it's the .MacAddress property of the selected NIC, not the .MacAddress property of a standard output of the Get-NetAdapter (or cousin) cmdlet, which is an array because it deals with all the NICs in the box.

I think I'm beginning to get the hang of this a little better now.