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!
Related
I'm attempting to create a query using PowerShell to count the number of Lenovo ThinkPad devices with a specific IP range.
Code derived from https://computergarage.org/how-to-create-sccm-device-collections-via-powershell.html
# Set Site Code
$SiteCode = Get-PSDrive -PSProvider CMSITE
Set-Location “$($SiteCode.Name):”
# Set Details
$DeviceCollectionName = "ThinkPads"
$FolderPath = “.\DeviceCollection\ThinkPads”
$Query = 'select * from SMS_R_System inner join SMS_G_System_NETWORK_ADAPTER_CONFIGURATION on SMS_G_System_NETWORK_ADAPTER_CONFIGURATION.ResourceId = SMS_R_System.ResourceId inner join SMS_G_System_COMPUTER_SYSTEM_PRODUCT on SMS_G_System_COMPUTER_SYSTEM_PRODUCT.ResourceId = SMS_R_System.ResourceId where SMS_G_System_NETWORK_ADAPTER_CONFIGURATION.IPAddress like
"10.1.%" and SMS_G_System_COMPUTER_SYSTEM_PRODUCT.Version like "%ThinkPad%"'
# Push Query
$NewCollection = Add-CMDeviceCollectionQueryMembershipRule -CollectionName $DeviceCollectionName -QueryExpression $Query -RuleName $DeviceCollectionName
Move-CMObject -FolderPath $FolderPath -InputObject $NewCollection
# Error
Add-CMDeviceCollectionQueryMembershipRule : No object corresponds to the specified parameters. At line:23 char:5
+ Add-CMDeviceCollectionQueryMembershipRule -CollectionName $Device ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Microsoft.Confi...yMembershipRule:AddDeviceCollectionQueryMembershipRule) [Add-CMDeviceCol...yMembershipRule], ItemNotFoundException
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.ConfigurationManagement.Cmdlets.Collections.Commands.AddDeviceCollectionQueryMembershipRule
Can someone explain what the error refers to? If I run the Add-CMDeviceCollectionQueryMembershipRule by itself, it requests all required info, then fails.
Figured it out. The command above requires you to have a Device Collection with the same name, and it will not auto create one for you. Added device collection with proper name and set, fixed everything.
I'm writing a script where in one part I want to export some settings of the SCVMM VM and set it in another. I have to run it from another machine, which doesn't have SCVMM installed so I have to call our VMM with Invoke-Command.
Unfortunately, variables I'm using in the code behave unexpectedly (don't want to say wrong, I assume it's by design). When they are used in parameter they don't transfer whole object that's in them, but just the Name.
$vm01 = Get-VM -Name VM01
$vm02 = Get-VM -Name VM02
$vm01name =$vm01.Name
$vm02name =$vm02.Name
$VMMparam = Invoke-Command –Computername VMM01 –ScriptBlock {
$VMMvm01=Get-SCVirtualMachine -VMMServer "VMM01.pandora.corp" -Name $using:vm01name
$vmcloud = $VMMvm01.Cloud
$vmos = $VMMvm01.OperatingSystem
$vmuserrole = $VMMvm01.UserRole
$vmowner = $VMMvm01.Owner
return $vmcloud,$vmos,$vmuserrole,$vmowner
}
$VMMcloud = $VMMparam[0]
$VMMos = $VMMparam[1]
$VMMuserrole = $VMMparam[2]
$VMMowner = $VMMparam[3]
Invoke-Command -ComputerName VMM01 -ScriptBlock {
if ($using:VMMcloud -eq $null){
Set-SCVirtualMachine -vm $using:vm02name -OperatingSystem $using:VMMos.Name
}
else{
Set-SCVirtualMachine -vm $using:vm02name -Cloud $using:VMMcloud -OperatingSystem $using:VMMos.Name -UserRole $using:VMMuserrole -Owner $using:VMMowner
}
}
This runs well until it's supposed to enter the Cloud object into the -cloud parameter. It ends in error:
Cannot bind parameter 'Cloud'. Cannot convert the "CLOUD01" value of type "Deserialized.Microsoft.SystemCenter.VirtualMachineManager.Cloud" to type "Microsoft.SystemCenter.VirtualMachineManager.Cloud".
+ CategoryInfo : InvalidArgument: (:) [Set-SCVirtualMachine], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.SystemCenter.VirtualMachineManager.Cmdlets.SetV
MCmdlet
+ PSComputerName : VMM01
If I call just the $using:VMMcloud variable inside of the Invoke-Command, it returns correctly. But when it's in parameter, just the Name value is returned. I tried it with arguments instead of prefixed variables, but same output.
Can you help me?
P.S. This is my first question in here. Hope the formatting is right and the problem described understandably. If not, ask away.
How can I concatenate this network path with this variable entered by the user (it will be a full network path)?
So the user type the new folder name, for example: Folder-123 (will be stored in the variable $pjname)
Copy-Item "\\SERVER\Work_3rd\R Drive Structure\Project No\MDCXXXX" -Destination "\\SERVER\Work_3rd" -Recurse
write-host "Folder has been created. Press any key to continue..."
[void][System.Console]::ReadKey($true)
Write-Host "Please enter the project name: "
$pjname = Read-Host
Write-Output "New Folder will be: $pjname"
Rename-Item -Path "\\SERVER\Work_3rd\MDCXXXX" -NewName $pjname
write-host "Folder has been renamed. Press any key to continue..."
[void][System.Console]::ReadKey($true)
$pathToTemplate = '\\SERVER\Work_3rd\R Drive Structure\Project No\MDCXXXX'
$rootPath2 = '\\SERVER\Work_3rd\'
$rootPath = -join ($rootPath2, $pjname) # this concatenates the new project
name on to the root folder path**
# $rootPath += $pjname # this concatenates the new project name on to the
root folder path
If(Test-Path $rootPath)
{
$CurrentACL = (Get-Item $pathToTemplate).GetAccessControl('Access')
$CurrentACL | Set-Acl -Path $rootPath
}
This new folder stored in $pjname should have a network path like \\\SERVER\Work-3rd\ + FOLDER NAME. For example \\\SERVER\Word-3rd\Folder-123
The PowerShell is not finding the final path of the new folder so the permission is not being applied on it.
I'm trying in a test area and getting this problem below:
Folder has been renamed. Press any key to continue...
Get-Acl : Cannot find path '\\SERVER\test-area\Test-123' because it does not exist.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV3.ps1:279 char:8
+ $acl = Get-Acl $NewNetworkPath
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-Acl], ItemNotFoundException
+ FullyQualifiedErrorId :
GetAcl_PathNotFound_Exception,Microsoft.PowerShell.Commands.GetAclCommand
You cannot call a method on a null-valued expression.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV3.ps1:282 char:1
+ $acl.SetAccessRule($rule)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Are you trying to create a new directory in that share path, or rename a directory? It looks like you're trying to rename a directory.
My guess it isn't working because you are missing the trailing "\" in your path. Here is some sample code that adds a user supplied variable to a network path:
$MyRootPath = "\\SomeServer\Dir1\Dir2\"
Write-Host "Enter Dir Name"
$myAnswer = Read-Host
I typed "hello" when prompted for new directory..
$finalAnswer = $myAnswer.Trim()
$NewNetworkPath = ("{0}{1}" -f $MyRootPath, $finalAnswer)
$NewNetworkPath
returns:
\\SomeServer\Dir1\Dir2\hello
Whenever you are concatenating paths, escpecially those supplied by end users, stick to a utility that can do most of the heavy lifting. Use the combine method as string concatenation has several pitfalls that have to be needlessly mitigated.
[io.path]::combine('\\server\share', 'newfolder')
The combine method will take the path parts, as an array, and build a proper path. Note this does not validate if the path exists. It can deal with trailing path separators well. These next commands yield the same result.
[io.path]::combine('\\server\share\', 'newfolder')
[io.path]::combine('\\server\share', 'newfolder')
Beware leading path separators though:
If the one of the subsequent paths is an absolute path, then the combine operation resets starting with that absolute path, discarding all previous combined paths.
Thanks Matt & oze4! A weird problem happened now. I used the solution of oze4 and sometimes it works and sometimes not. Would it be the string entered by the user?
When it worked the folder name was 'MDX1111 - XXXX Xxxxxxx Xxxxxxxxx' - 32 characters. I ran the code again using the folder name 'MDX1112 - Xxxxxx Xxxxxxx Xxxxxxxxxx' - 35 characters and got this error below:
Get-Acl : Cannot find path '\\SERVER\Work_Block_A\MDX1112 - Xxxxxx Xxxxxxx
Xxxxxxxxxx' because it does not
exist.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV1.ps1:125 char:8
+ $acl = Get-Acl $NewNetworkPath
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-Acl], ItemNotFoundException
+ FullyQualifiedErrorId :
GetAcl_PathNotFound_Exception,Microsoft.PowerShell.Commands.GetAclCommand
You cannot call a method on a null-valued expression.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV1.ps1:128 char:1
+ $acl.SetAccessRule($rule)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Any thoughts?
Thank you.
I am facing a problem how to add folder to existing ZIP file.
This zip file is created by PowerShell also.
I can only use system classes provided by Powershell 5. I cannot use any of user packages or plugins (7zip included).
Here is my code:
function addFileToArchiveTest ($filePathToAdd, $archivePathToUpdate) {
if ([System.IO.File]::Exists($filePathToAdd) -or (Test-Path $filePathToAdd)) {
$file = [System.IO.Path]::GetFileName($filePathToAdd);
Write-Host $filePathToAdd.Name;
Write-Host $filePathToAdd;
Write-Host $archivePathToUpdate;
$archive = [System.IO.Compression.ZipFile]::Open($archivePathToUpdate, [System.IO.Compression.ZipArchiveMode]::Update);
$compressionLevel = [System.IO.Compression.CompressionLevel]::NoCompression;
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive, $filePathToAdd, $file, "$compressionLevel");
$archive.Dispose();
} else {
Write-Host "[ERROR#function] <AddFileToArchive>: <filePathToAdd> does not exist!";
Write-Host "[ERROR#function] <Variable<filePathToAdd>>: $filePathToAdd";
Write-Host "[ERROR#function] <Variable<archivePathToUpdate>>: $archivePathToUpdate";
}
}
I am thinking about variable $file - there might be a problem, because folder doesn't have an extension.
I run script like this:
PS> addFileToArchiveTest "C:\TestFolder\FolderToArchive" "C:\TestFolder\thereIsAlreadyZipFile.zip"
It returns with error:
Exception calling "CreateEntryFromFile" with "4" argument(s): "Access to the
path 'C:\TestFolder\FolderToArchive' is denied."
At C:\Users\user\Desktop\testfolder.ps1:196 char:13
+ [System.IO.Compression.ZipFileExtensions]::CreateEntryFro ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : UnauthorizedAccessException
Noted I also try allow script and I am launching with admin rights.
Perhaps surprisingly, CreateEntryFromFile() is for adding files, not folders. You need to add each file individually:
Get-ChildItem $filePathToAdd | ForEach-Object {
[IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive, $_.FullName, $_.Name, "$compressionLevel")
}
As user #guiwhatsthat answered: PowerShell 5 does support Compress-Archive. It does exactly what you want.
That is working as I want.
I have a little problem here with my user profile path for the use with powershell.
I have set my profile.ps1 to this:
$Shell = Host.UI.RawUI
$Shell.WindowTitle="PowerShell obeys me!"
$Shell.BackgroundColor="White"
$Shell.ForegroundColor="Blue"
$size = $Shell.WindowSize
$size.width=120
$size.height=50
$Shell.WindowSize = $size
$size = $Shell.BufferSize
$size.width=120
$size.height=5000
$Shell.BufferSize = $size
but everytime i execute run poweshell, it shows some erors like this one:
Property 'WindowTitle' cannot be found on this object; make sure it exists and is settable.
At D:\data\d7bighs\Documents\WindowsPowerShell\profile.ps1:5 char:8
+ $Shell. <<<< WindowTitle="PowerShell obeys me!"
+ CategoryInfo : InvalidOperation: (WindowTitle:String) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
now is i check my profile it tells me this:
$profile
d:\data\myusername\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
but if i check this through windows explorer it tells me this:
d:\User\myusername\Documents\WindowsPowerShell\profile.ps1
Im confused here because within explorer tells me d:\Useres but PS shows it as d:\data...
How can i change this or force PS to look after d:\users instead of d:\data?
a tiny error: $Shell = $Host.UI.RawUI, and you may need to create the profile first by
New-Item -Path $PROFILE -Type file
then edit it by
notepad $PROFILE