Xavier Peña
Xavier Peña

Reputation: 7919

New-PSDrive's "-Persist" flag not working: drives removed on reboot

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

Upvotes: 4

Views: 5103

Answers (2)

Xavier Peña
Xavier Peña

Reputation: 7919

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]

Upvotes: 1

Cpt.Whale
Cpt.Whale

Reputation: 5351

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

Upvotes: 5

Related Questions