Renaming Computers with CSV files and Mac Address - powershell

I am trying to set up a master CSV file that gets checked when a PowerShell script runs during an MDT task sequence. What the script is trying to do is take the mac address of the physically connected nic card and compare it to the CSV files to see if it matches the Mac address that is saved in the file. If the mac address matches the value in the CSV it renames the computer to value that is paired with the Mac Address. If there is no match rename it based on the computer serial number.
Here is the what I have some far.
$computer = Get-WmiObject Win32_ComputerSystem -ComputerName $oldname
#$Machost is Mac Address of the active Network Card
$MacHost=Get-NetAdapter -Name "Ethernet" | Select Macaddress
$ConvertMacHost= $MacHost[0] -replace '(:|-|\.)'
Write-Host "You mac address is: " $MacHost
#MacHost gets reformatted to take out Semicolons - Ex 0011223344AA
#MacLab is a variable that stores the Computer name and Mac Address from File created in Deep Freeze that matches Mac Address of host.
#MacLab is formated to make all Alpha in Mac Address Capitalized
$MacLab=Import-Csv 'C:/projectcsv/LH_office.csv' | Select Workstations,#{Label ="Mac Address"; Expression ={$_."Mac Address".ToUpper()}}|where-object {$_."Mac Address" -eq $ConvertMacHost}|Select Workstations
#Checks to see if Host Mac Address Matches CSV File
If ([string]::IsNullorEmpty($MacLab))
{
Write-Warning -Message "No Mac Address found in our Records that Match the Host Machine Mac Address: Exit code 11001"
write-host ""
exit 11001
}Else{
$strUser = "******\domainadd"
$strDomain = "******"
$strPassword = ConvertTo-SecureString "*******" -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PsCredential $strUser,
$strPassword
#---- Define Prefix to be used -----#
$strPrefix='LH'
$strEnding='DESK'
#----- Get Serial Number ------#
$SN=(gwmi win32_BIOS).serialnumber
#--If working with VM, the SN length is longer than 15 Char------#
if($SN.length -gt 11) {
#Get the last 7 Char of Serial
$StrName=($SN.substring($SN.length - 11)) -replace "-",""
$strComputerName=$strPrefix+"-"+$StrName+"-"+$strEnding
} else {$strComputerName=$strPrefix+"-"+$SN+"-"+$strEnding
}
#------ Rename the Computer using WMI --------#
#$computer=gwmi win32_computersystem
#$computer.rename($strComputerName)
#-----Powershell 5 above use-------#
rename-computer -NewName $StrComputerName -PassThru -DomainCredential $Credentials
When I run the code I get this error message:
I added the computer I was working to the csv and still get the error. I know later I have to add the function where it checks to see if the name is already in AD. but want to get it working first. Please any help you can give would be helpful.
Edit: Here is a picture of the CSV file that shows what information is in there.
After making the corrections suggested I get these errors when the scripts run.
At \\LH-WDS-MDT\DeploymentShare$\Scripts\CSV_Computer_Rename_LH.ps1:7 char:13
+ $DriveMap = New-PSDrive -Name "M" -Root "\\LH-WDS-MDT\CSV_Files" -PSP ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TaskSequencePSHost 7/13/2020 2:24:53 PM 0 (0x0000)
InvalidOperation: (M:PSDriveInfo) [New-PSDrive], Win32Exception TaskSequencePSHost 7/13/2020 2:24:54 PM 0 (0x0000)
Exception calling "Substring" with "1" argument(s): "StartIndex cannot be less than zero.
Parameter name: startIndex" TaskSequencePSHost 7/13/2020 2:24:55 PM 0 (0x0000)
At \\LH-WDS-MDT\DeploymentShare$\Scripts\CSV_Computer_Rename_LH.ps1:12 char:1
+ $CompNameSerial = ($CompSerialNumber.substring($CompSerialNumber.leng ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TaskSequencePSHost 7/13/2020 2:24:55 PM 0 (0x0000)
NotSpecified: (:) [], MethodInvocationException TaskSequencePSHost 7/13/2020 2:24:55 PM 0 (0x0000)
Skip computer 'ADMINIS-8EMV500' with new name '#{Workstations=LH-TestMo-5040}' because the new name is not valid. The new computer name entered is not properly formatted. Standard names may contain letters (a-z, A-Z), numbers (0-9), and hyphens (-), but no spaces or periods (.). The name may not consist entirely of digits, and may not be longer than 63 characters. TaskSequencePSHost 7/13/2020 2:25:03 PM 0 (0x0000)
At \\LH-WDS-MDT\DeploymentShare$\Scripts\CSV_Computer_Rename_LH.ps1:29 char:5
+ rename-computer -NewName $NameComputerCSV -PassThru -DomainCreden ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TaskSequencePSHost 7/13/2020 2:25:03 PM 0 (0x0000)
InvalidArgument: (#{Workstations=LH-TestMo-5040}:String) [Rename-Computer], InvalidOperationException TaskSequencePSHost 7/13/2020 2:25:03 PM 0 (0x0000)

$MacHost is receiving an object with properties, so you need to specify the property you want or you get weird results, which is causing Where-Object to fail.
Specifically, $ConvertMacHost ended up being something like #{MacAddress=9CB6D0959AE5}
This line
$ConvertMacHost= $MacHost[0] -replace '(:|-|\.)'
should be
$ConvertMacHost= $MacHost[0].MacAddress -replace '(:|-|\.)'

Related

Using CVS file to map network drives - PowerShell

I am developing a couple of PowerShell scripts to help speed up the process of migrating user data from an old workstation to a new one. Currently trying to make one to help with retrieving and then deploying mapped network drives and have hit a snagged with the deployment aspect. I am new to PowerShell and learning as I go along using the ISE to help spot some of the problem areas the script has. Here is a copy of what the script currently looks like and the error I am receiving when trying to run it on the machine.
# Import drive list.
$mappedDrives = Import-Csv C:\Users\########\Desktop\WIP_Scripts\MasterCopy\mappedDrives.csv
$mappedDrives | %{$_ -replace ":"}
foreach ($Name in $mappedDrives) {
New-PSDrive -Name $Name.Name -PSProvider FileSystem -Root "ProviderName" -Persist -ErrorAction Continue
}
Once I have it working Ill make the edits for where the import comes from. The errors I am currently receiving are:
New-PSDrive : Cannot process the drive name because the drive name contains one or more of
the following characters that are not valid: ; ~ / \ . :
At C:\Users\#######\Desktop\WIP_Scripts\MasterCopy\ImportMappedDrives.ps1:8 char:5
+ New-PSDrive -Name $Name.Name -PSProvider FileSystem -Root "Provid ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-PSDrive], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.NewPSDriveCommand
New-PSDrive : Cannot process the drive name because the drive name contains one or more of
the following characters that are not valid: ; ~ / \ . :
At C:\Users\#######\Desktop\WIP_Scripts\MasterCopy\ImportMappedDrives.ps1:8 char:5
+ New-PSDrive -Name $Name.Name -PSProvider FileSystem -Root "Provid ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-PSDrive], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.NewPSDriveCommand
New-PSDrive : Cannot process the drive name because the drive name contains one or more of
the following characters that are not valid: ; ~ / \ . :
At C:\Users\#######\Desktop\WIP_Scripts\MasterCopy\ImportMappedDrives.ps1:8 char:5
+ New-PSDrive -Name $Name.Name -PSProvider FileSystem -Root "Provid ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-PSDrive], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.NewPSDriveCommand
For the script used to retrieve the drive information:
$mappedDrives = #()
$Name = Get-WmiObject -ClassName Win32_MappedLogicalDisk | Select Name, ProviderName
foreach ($Name in $Name) {
if ($Name. ProviderName) {
$mappedDrives += Select-Object Name, ProviderName -InputObject $Name
}
}
$mappedDrives | Export-Csv mappedDrives.csv
MappedDrives.csv Output
Also attached is what the mappeddrives.csv output looks like for the retrieval. I thought that the csv file may be causing the invalid character arguements since the Name found within the csv file includes the ":" character. Also I am a bit confused on whether or not it will be able to see the "ProviderName" within the csv file or if I need to declare it in order for the script to add it to its argument. Again I am extremely new to Powershell so lots of what I have written down is what I have found from this site, Microsoft, or other blogs/forums and trying to Frankenstein together a working script. Any feedback on how to improve or get this to work and/or why using another method would be better in this situation would be greatly appreciated.
###Revision 1###
Utilizing the new script provided by RetiredGeek
# Import drive list.
$CFSArgs = #{PropertyNames = "Name", "ProviderName"
Delimiter = ','}
$MappedDrives = (Get-Content "G:\BEKDocs\Scripts\Test\mappedDrives.csv") |
ConvertFrom-String #CFSArgs
for ($cnt = 1; $cnt -lt $MappedDrives.count; $cnt++) {
$NPSDArgs =
#{Name = $(($MappedDrives[$cnt].Name).Substring(0,1))
PSProvider = "FileSystem"
Root = "$($MappedDrives[$cnt].ProviderName)"
Persist = $True
ErrorAction = "Continue"
}
New-PSDrive #NPSDArgs
}
I am now receiving the following error:
New-PSDrive : When you use the Persist parameter, the root must be a file system location
on a remote computer.
At C:\Users\######\Desktop\MasterCopy\Test2.ps1:16 char:9
+ New-PSDrive #NPSDArgs
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (":PSDriveInfo) [New-PSDrive], NotSupported
Exception
+ FullyQualifiedErrorId : DriveRootNotNetworkPath,Microsoft.PowerShell.Commands.NewPSD
riveCommand
The two questions I have now are:
Would it be more appropriate to use "Net use" instead of "New-PSDrive" for what I am trying to achieve(which is mapping a network drive to a computer using the cvs file created)?
If the use of the New-PSDrive cmdlet is a non-issue how do I rectify the error the script is currently outputting?
Thanks again to RetiredGeek and Theo for your inputs.
FT,
You need to evaluate your arguments in the New-PSDrive line.
Using Substring there eliminates code and makes it more efficient.
I had problems using Import-CSV so I switched to Get-Content and adjusted
# Import drive list.
$CFSArgs = #{PropertyNames = "Name", "ProviderName"
Delimiter = ','}
$MappedDrives = (Get-Content "G:\BEKDocs\Scripts\Test\mappedDrives.csv") |
ConvertFrom-String #CFSArgs
for ($cnt = 1; $cnt -lt $MappedDrives.count; $cnt++) {
$NPSDArgs =
#{Name = $(($MappedDrives[$cnt].Name).Substring(0,1))
PSProvider = "FileSystem"
Root = "$($MappedDrives[$cnt].ProviderName)"
Scope = "Global"
Persist = $True
ErrorAction = "Continue"
}
New-PSDrive #NPSDArgs -WhatIf
}
I used the -WhatIf parameter since I don't have your targets available but it showes what would have been done.
Output:
What if: Performing the operation "New drive" on target "Name: X Provider: Micro
soft.PowerShell.Core\FileSystem Root: \\hapi\desktop$\Decommission Log".
What if: Performing the operation "New drive" on target "Name: Y Provider: Micro
soft.PowerShell.Core\FileSystem Root: \\gonzo\Temp\AZ".
What if: Performing the operation "New drive" on target "Name: Z Provider: Micro
soft.PowerShell.Core\FileSystem Root: \\gonzo\Temp\001".
PS>
Update 1:
Further testing on my network (Peer-to-Peer) reveals that adding the Scope parameter (see above) will create the mapping, even though you get the same message, and it will last until you Reboot! It does NOT however persist after the reboot so it is not doing what it should. I still don't understand why it the message is displayed as the root is on another computer. Also, the mapping doesn't show in File Explorer although I can open a command prompt and successfully do a DIR on the drive.
Update 2:
I tried another test mapping to my Synology NAS and it worked w/o the error message. But, alas it did NOT persist a reboot!

Separating values entered into a string

So I am trying to create a Powershell menu that when the user selects a choice, it will ask for the value or values its trying to search (ex. Ping Multiple Computers). I am currently having a hard time getting that to work. I will post pictures to show what I mean
When I type in one name to search the command executes fine shown below:
When I try with multiple values it doesn't work:
Here is a snap of the code I have:
Any help of course is much appreciated.
UPDATE - 11/13
This is what I currently have:
function gadc {
Param(
[Parameter(Mandatory=$true)]
[string[]] $cname # Note: [string[]] (array), not [string]
)
$cname = "mw$cname"
Get-ADComputer $cname
}
This is the output in the Console
cmdlet gadc at command pipeline position 1
Supply values for the following parameters:
cname[0]: imanuel
cname[1]: troyw
cname[2]: hassan
cname[3]:
Get-ADComputer : Cannot convert 'System.String[]' to the type
'Microsoft.ActiveDirectory.Management.ADComputer' required by parameter 'Identity'. Specified
method is not supported.
At line:32 char:19
+ Get-ADComputer $cname
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management.Commands.G
etADComputer
Press Enter to continue...:
**And here is the other way with the same result:**
cmdlet gadc at command pipeline position 1
Supply values for the following parameters:
cname[0]: imanuel, troyw
Get-ADComputer : Cannot convert 'System.String[]' to the type
'Microsoft.ActiveDirectory.Management.ADComputer' required by parameter 'Identity'. Specified
method is not supported.
At line:32 char:19
+ Get-ADComputer $cname
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management.Commands.G
etADComputer
Press Enter to continue...:
You need to declare your mandatory parameter as an array, then PowerShell's automatic prompting will allow you to enter multiple values, one by one - simply press Enter by itself after having submitted the last value in order to continue:
function gadc {
param(
[Parameter(Mandatory)]
[string[]] $cname # Note: [string[]] (array), not [string]
)
# Get-ADComputer only accepts one computer name at a time
# (via the positionally implied -Identity parameter), so you must loop
# over the names.
# The following should work too, but is slightly slower:
# $cname | Get-ADComputer
foreach ($c in $cname) { Get-ADComputer $c }
}

How to get this PowerShell command output into an iterable collection or array

This answer shows how to output a list of computer-names on the domain:
(([adsi]"WinNT://$((Get-WMIObject Win32_ComputerSystem).Domain)").Children).Where({$_.schemaclassname -eq 'computer'})
If the Pathproperty is appended
... .Where({$_.schemaclassname -eq 'computer'}).Path
it outputs a list in this format:
WinNT://{domainname}.net/{machine-name}
How would that succession of fully qualified machine names be made into an array or a collection that could be iterated by the script found here?
Can that command (i.e. its output) simply be assigned to an array variable?
[array]$ComputerNames = ... (([adsi]"WinNT://$((Get-WMIObject <snip> ).Path
I suspect not, since when I try that
$ComputerNames = ... (([adsi]"WinNT://$((Get-WMIObject <snip> ).Path
foreach($computer in $ComputerNames) {
if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer)) {
I get an error:
Exception calling "OpenRemoteBaseKey" with "2" argument(s): "The network path was not found."
At C:\Users\{myusername}\Get-NetFrameworkVersion.ps1:40 char:8
+ if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Lo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IOException
My ultimate goal is to obtain the .NET versions installed on all PCs on the domain.

Putting together an ADSI LDAP query

I am searching through the active directory for users under a specific organisation unit, that I would like to change using ADSI.
# get all users from the organizational unit
$accounts = Get-ADObject -filter 'objectClass -eq "user"' -SearchBase $dsn
# iterate over user objects
foreach ($account in $accounts) {
# unfortunately we have to use ADSI over the set-aduser cmdlet as we neeed to touch remote desktop attribues
$user = [ADSI]"LDAP://" + ($account.DistinguishedName).ToString()
# get logon name
$SamAccountName = $user.psbase.InvokeGet("SamAccountName")
# Profile Attributes
$user.psbase.InvokeSet("ProfilePath", "")
$user.psbase.InvokeSet("ScriptPath", "DIR\Logon.cmd")
$user.psbase.InvokeSet("HomeDrive", "H:")
$user.psbase.InvokeSet("HomeDirectory", "\\host\users$\${SamAccountName}")
# Remote Desktop Services Attributes
$user.psbase.InvokeSet("TerminalServicesProfilePath", "")
$user.psbase.InvokeSet("TerminalServicesHomeDirectory", "\\host\users$\${SamAccountName}")
$user.psbase.InvokeSet("TerminalServicesHomeDrive", "H:")
# Write attributes back to global catalog
$user.SetInfo()
}
This all works fine, until it comes to the $user = [ADSI]"LDAP://" + ($account.DistinguishedName).ToString() part.
Method invocation failed because [System.DirectoryServices.DirectoryEntry] does not contain a method named 'op_Addition'.
At \\tsclient\D\SourceCode\PowerShell\Set-ADUserAttributes.ps1:37 char:5
+ $user = [ADSI]"LDAP://" + ($account.DistinguishedName).ToString()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Exception calling "InvokeGet" with "1" argument(s): "Unspecified error
"
At \\tsclient\D\SourceCode\PowerShell\Set-ADUserAttributes.ps1:40 char:5
+ $SamAccountName = $user.psbase.InvokeGet("SamAccountName")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
It seems there is no query getting executed. However, $account.DistinguishedName contains the correct LDAP path (which I have tested manually).
So what am I doing wrong here?.
You're trying to append to an ADSI object by casting "LDAP://" as [ADSI] before you do the append.
Cat your strings first, then do the cast:
$user = [ADSI]("LDAP://" + $account.DistinguishedName)
The casting operation has higher precedence than the concatenation operation, so you need to do the concatenation in a subexpression, either like this:
[adsi]("LDAP://" + $account.DistinguishedName)
or like this:
[adsi]"LDAP://$($account.DistinguishedName)"
The distinguished name is automatically converted to a string here, so you don't need to manually call ToString().

Powershell error - GetHostByAddress

Stumped on a problem here - running Powershell 1.0
Code (Assume an ip address which is valid is being passed in):
$ips = #(Import-CSV $attachmentLocation)
foreach ($ip in $ips){
$ipAddress = $ip.IPAddress
$length = $ipaddress.length
write-host "Length is: ($length)"
if(Test-Connection -ComputerName $ipAddress -Count 1 -ea silentlycontinue) {
write-host $ipAddress
$hostData = ([Net.Dns]::GetHostByAddress($ipAddress)).HostName
}
}
Output:
Length is: (11)
10.xx.xx.xx
Exception calling "GetHostByAddress" with "1" argument(s): "The requested name is valid, but no data of the requested type was found"
At FileName:13 char:43
+ $hostData = ([Net.Dns]::GetHostByAddress <<<< ($ipAddress)).HostName
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
If I run the following it works fine - seems to be data type issue (im passing in a string value):
$hostData = ([Net.Dns]::GetHostByAddress("10.xx.xx.xx")).HostName
Working Code:
$ipAddress = "10.xx.xx.xx"
$hostData = ([Net.Dns]::GetHostByAddress($ipAddress)).HostName
Answer:
Issue was with the ActiveDirectory Domain DNS resolution not the command, while some IP addresses were pingable they were not resolving properly on machine where the script was run. This was causing the error 'no data of the requested type was found' refers to the fact it couldn't resolve the IP to a DNS name.
I've got two ideas you could try:
GetHostByAddress() supports string and ipaddress. So try casting to ipaddress-type before running the function.
if(Test-Connection -ComputerName $ipAddress -Count 1 -ea silentlycontinue) {
write-host $ipAddress
$hostData = ([Net.Dns]::GetHostByAddress(([ipaddress]$ipAddress))).HostName
}
If you're on PS 1.0, then your first priority should be to upgrade the machines to at least PowerShell 2.0. Nothing works well in PS 1.0.
Running PS3, I get the error shown when traversing subnets on our domain. Especially remote locations.
I'm checking 5 different C-class subnets on our domain to make sure there aren't devices we don't have in AD.
It's also possible some devices aren't PCs with a hostname: routers, switches, firewalls, scanners, and the like.
When my code gets to my local office, no error.
I'm not using a file, instead I generate the subnets via code in the script.