4

The script mounts the drive correctly, but the drive is not persisted after rebooting the machine:

function RemapDrive {
    param(      
        $DriveLetter,
        $FullPath,
        $Credential     
    )
    
    Write-Host "Trying to remove $DriveLetter in case it already exists ..."    
    # $DriveLetter must be concatenated with ":" for the command to work
    net use "${DriveLetter}:" /del
    
    ## $DriveLetter cannot contain ":"
    $psDrive = New-PSDrive -Name "$DriveLetter" -PSProvider "FileSystem" -Root "$FullPath" -Credential $Credential -Scope "Global" -Persist
    
    Write-Host "$DriveLetter was successfully added !"  
}

function BuildCredential {
    param (
        $Username,
        $Password
    )
    $pass = ConvertTo-SecureString $Password -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential ($Username, $pass)
    return $credential
}

$credential = (BuildCredential -Username "xxxxxx" -Password "yyyyyy")[-1]

RemapDrive -DriveLetter "X" -FullPath "\\my-server\x" -Credential $credential

What I have found:

“When you scope the command locally, that is, without dot-sourcing, the Persist parameter does not persist the creation of a PSDrive beyond the scope in which you run the command. If you run New-PSDrive inside a script, and you want the new drive to persist indefinitely, you must dot-source the script. For best results, to force a new drive to persist, specify Global as the value of the Scope parameter in addition to adding Persist to your command.”

I have tried executing the script with ". .\my-script.ps1" (to dot-source the script?), but the result is the same.

Playing around with "net use" and the registry to try to add the network drive has lead me to a cul-de-sac as well.


Specs:

Windows 10 Home

Powershell version:

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      18362  1171
3
  • stackoverflow.com/questions/32865461/… does this help Commented Feb 8, 2021 at 16:46
  • @TheGameiswar "You need to map the drives from a PowerShell session running as the same user that is logged into Windows" <-- I was already doing that (I have even checked with $env:UserName in case I was missing something obvious). But your link might help other people that finds my question. Commented Feb 8, 2021 at 16:49
  • 1
    We ran into the same issue here at work but, we got it sorted; I just don't recall how but, i will search for our script that got it working. Think it was something as simple as providing "Yes" after -Persist. Commented Feb 8, 2021 at 17:17

2 Answers 2

5

Basically, New-PSDrive doesn't have the /SAVECRED parameter from net use, and will not persistently map drives as a user other than the one running the script.

There are three ways to handle this:

  1. [Recommended] Fix the file share permissions instead of using a separate username/password, then use New-PSDrive -Name "$DriveLetter" -PSProvider "FileSystem" -Root "$FullPath" -Scope 'Global' -Persist with no credential flag. This assumes your file share allows kerberos logins, so may not work in some edge cases.
  2. Use net use, and include the username, password, /persistent:yes and /savecred. This can be done in powershell without any issues.
  3. Set the powershell script you already have to run at startup.
  4. Set up your script to use the credential manager - see the answer here
  • Install the CredentialManager powershell module
  • set HKCU\Network\[drive letter]\ConnectionType = 1
  • set HKCU\Network\[drive letter]\DeferFlags= 4
2
  • Thanks a lot for the detailed answer. About #1, we are stuck using a single smb user, for reasons that are outside my control. #2 is not trivial (using a script): username + password + /persistent:yes + /savecred throws a "conflicting switches" error. The solution seems to be using cmdkey combined with net use, but that was not trivial either (will retry though). #3 is of course an option, but I don't like having the plaintext password in each user's machine. I don't know about #4, I'll need to do some research. Nice summary, thanks again! Commented Feb 9, 2021 at 7:30
  • Option #2 ended up working ! With some extra steps, but it works: I have a added a full answer below to show the script. Thanks a lot, I was a bit lost regarding those authentication + registration issues, and you answer helped to clarify those tricky things. Commented Feb 9, 2021 at 10:46
1

What finally work was user19702's option #2, with a bit of extra work regarding the registration of the username and the password.

WARNING: as he mentioned, the best option (option #1) would have been "fixing the file share permissions instead of using a separate username/password". This was not possible in my case, and this is why I had to go with option #2.

This is the script:

# ---
# Helper functions:

function RemapDrive {
    param(      
        $DriveLetter,
        $Server,
        $FullPath,
        $Credential     
    )
    

    # For net.exe to work, DriveLetter must end with with ":"

    Write-Host "Trying to remove $DriveLetter in case it already exists ..."        
    net use "$DriveLetter" /del
    
    # "net use" requires username and password as plain text
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password)
    $Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    $Username=$Credential.Username
    
    Write-Host "Registring credentials for server '$Server' ..."    
    cmdkey /add:$Server /user:$Username /pass:$Password
    
    Write-Host "Mapping the drive ..."
    net use $DriveLetter $FullPath /persistent:yes i
    
    Write-Host "$DriveLetter was successfully added !"  
}

function BuildCredential {
    param (
        $Username,
        $Password
    )
    $pass = ConvertTo-SecureString $Password -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential ($Username, $pass)
    return $credential
}


# ---
# Process to execute:

$credential = (BuildCredential -Username "xxxxxx" -Password "yyyyyy")[-1]

RemapDrive -DriveLetter "X:" -Server "my-server" -FullPath "\\my-server\x" -Credential $credential

If you do not want to use a hardcoded password in BuildCredential, but you want to prompt the user instead:

function GetCredential {
    param(
        $Label
    )
    $credential = Get-Credential -Message "Write your credentials for '$Label':"
    if(!$credential) {
        throw "A credential was needed to continue. Process aborted."
    }       
    return $credential
}

Also, if instead of using $Server as a param, you want to extract it from $FullPath using regex, you can do that.

It presumes the $FullPath has the following format: \\server-name\dir1\dir2\etc

    # Get server name using regex:
    $FullPath -match '\\\\(.*?)\\.*?'
    $Server = $Matches[1]
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.