Forum Discussion

arturorosario's avatar
arturorosario
Copper Contributor
Mar 09, 2022

Map network drive

How can I map an internal network share as a user home folder with Azure AD joined PCs?

 

This is what I'm trying to do:

 

H Drive

\\server\students\%username%

 

Any help would be most appreciated

  • LainRobertson's avatar
    LainRobertson
    Silver Contributor

    arturorosario 

     

    One quick addition that the script heading gave a hint to.

     

    I looked up this InTune generator and you need to be sure that in the "UNC Path" field you use the format of:

    \\diskstation\student\$env:username

     

    And not:

    \\diskstation\student\%username%

     

    The former will suit PowerShell. The latter will suit DOS.

     

    But again, this should feature somewhere within the PowerShell script that was generated, and we can't currently see that.

     

    Cheers,

    Lain

    • arturorosario's avatar
      arturorosario
      Copper Contributor
      Thank you for your kind help. Here is the script I'm using:

      <#
      .DESCRIPTION
      This script performs network drive mappings with PowerShell and is auto generated by the intune-drive-mapping-generator (https://intunedrivemapping.azurewebsites.net).
      When executed under SYSTEM authority a scheduled task is created to ensure recurring script execution on each user logon.

      .NOTES
      Author: Nicola Suter, nicolonsky tech: https://tech.nicolonsky.ch
      #>

      [CmdletBinding()]
      Param()

      ###########################################################################################
      # Start transcript for logging
      ###########################################################################################

      Start-Transcript -Path $(Join-Path $env:temp "DriveMapping.log")

      ###########################################################################################
      # Input values from generator
      ###########################################################################################

      $driveMappingJson = '[{"Path":"\\\\diskstation.musowls.org\\Students\\%username%","DriveLetter":"H","Label":"H_Drive","Id":1,"GroupFilter":null}]'

      $driveMappingConfig = $driveMappingJson | ConvertFrom-Json -ErrorAction Stop
      #used to create an array for groups
      $driveMappingConfig = foreach ($d in $driveMappingConfig) {
      [PSCustomObject]@{
      Path = $($d.Path)
      DriveLetter = $($d.DriveLetter)
      Label = $($d.Label)
      Id = $($d.Id)
      GroupFilter = $($d.GroupFilter -split ",")
      }
      }

      # Override with your Active Directory Domain Name e.g. 'ds.nicolonsky.ch' if you haven't configured the domain name as DHCP option
      $searchRoot = "musowls.org"

      # If enabled all mounted PSdrives from filesystem except os drives get disconnected if not specified in drivemapping config
      $removeStaleDrives = $false

      ###########################################################################################
      # Helper function to determine a users group membership
      ###########################################################################################

      # Kudos for Tobias Renström who showed me this!
      function Get-ADGroupMembership {
      param(
      [parameter(Mandatory = $true)]
      [string]$UserPrincipalName
      )

      process {

      try {

      if ([string]::IsNullOrEmpty($env:USERDNSDOMAIN) -and [string]::IsNullOrEmpty($searchRoot)) {
      Write-Error "Security group filtering won't work because `$env:USERDNSDOMAIN is not available!"
      Write-Warning "You can override your AD Domain in the `$overrideUserDnsDomain variable"
      }
      else {

      # if no domain specified fallback to PowerShell environment variable
      if ([string]::IsNullOrEmpty($searchRoot)) {
      $searchRoot = $env:USERDNSDOMAIN
      }

      $searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher
      $searcher.Filter = "(&(userprincipalname=$UserPrincipalName))"
      $searcher.SearchRoot = "LDAP://$searchRoot"
      $distinguishedName = $searcher.FindOne().Properties.distinguishedname
      $searcher.Filter = "(member:1.2.840.113556.1.4.1941:=$distinguishedName)"

      [void]$searcher.PropertiesToLoad.Add("name")

      $list = [System.Collections.Generic.List[String]]@()

      $results = $searcher.FindAll()

      foreach ($result in $results) {
      $resultItem = $result.Properties
      [void]$List.add($resultItem.name)
      }

      $list
      }
      }
      catch {
      #Nothing we can do
      Write-Warning $_.Exception.Message
      }
      }
      }

      #check if running as system
      function Test-RunningAsSystem {
      [CmdletBinding()]
      param()
      process {
      return [bool]($(whoami -user) -match "S-1-5-18")
      }
      }


      #Testing if groupmembership is given for user
      function Test-GroupMembership {
      [CmdletBinding()]
      param (
      $driveMappingConfig,
      $groupMemberships
      )
      try {
      $obj = foreach ($d in $driveMappingConfig) {
      if (-not ([string]::IsNullOrEmpty($($d.GroupFilter)))) {
      foreach ($filter in $($d.GroupFilter)) {
      if ($groupMemberships -contains $filter) {
      $d
      }
      else {
      #no match for group
      }
      }
      }
      else {
      $d
      }
      }
      $obj
      }
      catch {
      Write-Error "Unknown error testing group memberships: $($_.Exception.Message)"
      }
      }

      ###########################################################################################
      # Get current group membership for the group filter capabilities
      ###########################################################################################

      Write-Output "Running as SYSTEM: $(Test-RunningAsSystem)"

      if ($driveMappingConfig.GroupFilter) {
      try {
      #check if running as user and not system
      if (-not (Test-RunningAsSystem)) {

      $groupMemberships = Get-ADGroupMembership -UserPrincipalName $(whoami -upn)
      }
      }
      catch {
      #nothing we can do
      }
      }
      ###########################################################################################
      # Mapping network drives
      ###########################################################################################
      #Get PowerShell drives and rename properties

      if (-not (Test-RunningAsSystem)) {

      $psDrives = Get-PSDrive | Where-Object { $_.Provider.Name -eq "FileSystem" -and $_.Root -notin @("$env:SystemDrive\", "D:\") } `
      | Select-Object @{N = "DriveLetter"; E = { $_.Name } }, @{N = "Path"; E = { $_.DisplayRoot } }

      # only map drives where group membership applicable
      $driveMappingConfig = Test-GroupMembership -driveMappingConfig $driveMappingConfig -groupMemberships $groupMemberships

      #iterate through all network drive configuration entries
      foreach ($drive in $driveMappingConfig) {

      try {
      #check if variable in unc path exists, e.g. for $env:USERNAME -> resolving
      if ($drive.Path -match '\$env:') {
      $drive.Path = $ExecutionContext.InvokeCommand.ExpandString($drive.Path)
      }

      #if label is null we need to set it to empty in order to avoid error
      if ($null -eq $drive.Label) {
      $drive.Label = ""
      }

      $exists = $psDrives | Where-Object { $_.Path -eq $drive.Path -or $_.DriveLetter -eq $drive.DriveLetter }
      $process = $true

      if ($null -ne $exists -and $($exists.Path -eq $drive.Path -and $exists.DriveLetter -eq $drive.DriveLetter )) {
      Write-Output "Drive '$($drive.DriveLetter):\' '$($drive.Path)' already exists with correct Drive Letter and Path"
      $process = $false

      }
      else {
      # Mapped with wrong config -> Delete it
      Get-PSDrive | Where-Object { $_.DisplayRoot -eq $drive.Path -or $_.Name -eq $drive.DriveLetter } | Remove-PSDrive -EA SilentlyContinue
      }

      if ($process) {
      Write-Output "Mapping network drive $($drive.Path)"
      $null = New-PSDrive -PSProvider FileSystem -Name $drive.DriveLetter -Root $drive.Path -Description $drive.Label -Persist -Scope global -EA Stop
      (New-Object -ComObject Shell.Application).NameSpace("$($drive.DriveLetter):").Self.Name = $drive.Label
      }
      }
      catch {
      $available = Test-Path $($drive.Path)
      if (-not $available) {
      Write-Error "Unable to access path '$($drive.Path)' verify permissions and authentication!"
      }
      else {
      Write-Error $_.Exception.Message
      }
      }
      }

      # Remove unassigned drives
      if ($removeStaleDrives -and $null -ne $psDrives) {
      $diff = Compare-Object -ReferenceObject $driveMappingConfig -DifferenceObject $psDrives -Property "DriveLetter" -PassThru | Where-Object { $_.SideIndicator -eq "=>" }
      foreach ($unassignedDrive in $diff) {
      Write-Warning "Drive '$($unassignedDrive.DriveLetter)' has not been assigned - removing it..."
      Remove-SmbMapping -LocalPath "$($unassignedDrive.DriveLetter):" -Force -UpdateProfile
      }
      }

      # Fix to ensure drives are mapped as persistent!
      $null = Get-ChildItem -Path HKCU:\Network -ErrorAction SilentlyContinue | ForEach-Object { New-ItemProperty -Name ConnectionType -Value 1 -Path $_.PSPath -Force -ErrorAction SilentlyContinue }
      }

      ###########################################################################################
      # End & finish transcript
      ###########################################################################################

      Stop-transcript

      ###########################################################################################
      # Done
      ###########################################################################################

      #!SCHTASKCOMESHERE!#

      ###########################################################################################
      # If this script is running under system (IME) scheduled task is created (recurring)
      ###########################################################################################

      if (Test-RunningAsSystem) {

      Start-Transcript -Path $(Join-Path -Path $env:temp -ChildPath "IntuneDriveMappingScheduledTask.log")
      Write-Output "Running as System --> creating scheduled task which will run on user logon"

      ###########################################################################################
      # Get the current script path and content and save it to the client
      ###########################################################################################

      $currentScript = Get-Content -Path $($PSCommandPath)

      $schtaskScript = $currentScript[(0) .. ($currentScript.IndexOf("#!SCHTASKCOMESHERE!#") - 1)]

      $scriptSavePath = $(Join-Path -Path $env:ProgramData -ChildPath "intune-drive-mapping-generator")

      if (-not (Test-Path $scriptSavePath)) {

      New-Item -ItemType Directory -Path $scriptSavePath -Force
      }

      $scriptSavePathName = "DriveMapping.ps1"

      $scriptPath = $(Join-Path -Path $scriptSavePath -ChildPath $scriptSavePathName)

      $schtaskScript | Out-File -FilePath $scriptPath -Force

      ###########################################################################################
      # Create dummy vbscript to hide PowerShell Window popping up at logon
      ###########################################################################################

      $vbsDummyScript = "
      Dim shell,fso,file

      Set shell=CreateObject(`"WScript.Shell`")
      Set fso=CreateObject(`"Scripting.FileSystemObject`")

      strPath=WScript.Arguments.Item(0)

      If fso.FileExists(strPath) Then
      set file=fso.GetFile(strPath)
      strCMD=`"powershell -nologo -executionpolicy ByPass -command `" & Chr(34) & `"&{`" &_
      file.ShortPath & `"}`" & Chr(34)
      shell.Run strCMD,0
      End If
      "

      $scriptSavePathName = "IntuneDriveMapping-VBSHelper.vbs"

      $dummyScriptPath = $(Join-Path -Path $scriptSavePath -ChildPath $scriptSavePathName)

      $vbsDummyScript | Out-File -FilePath $dummyScriptPath -Force

      $wscriptPath = Join-Path $env:SystemRoot -ChildPath "System32\wscript.exe"

      ###########################################################################################
      # Register a scheduled task to run for all users and execute the script on logon
      ###########################################################################################

      $schtaskName = "IntuneDriveMapping"
      $schtaskDescription = "Map network drives from intune-drive-mapping-generator."

      $trigger = New-ScheduledTaskTrigger -AtLogOn
      #Execute task in users context
      $principal = New-ScheduledTaskPrincipal -GroupId "S-1-5-32-545" -Id "Author"
      #call the vbscript helper and pass the PosH script as argument
      $action = New-ScheduledTaskAction -Execute $wscriptPath -Argument "`"$dummyScriptPath`" `"$scriptPath`""
      $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries

      $null = Register-ScheduledTask -TaskName $schtaskName -Trigger $trigger -Action $action -Principal $principal -Settings $settings -Description $schtaskDescription -Force

      Start-ScheduledTask -TaskName $schtaskName

      Stop-Transcript
      }

      ###########################################################################################
      # Done
      ###########################################################################################
      • LainRobertson's avatar
        LainRobertson
        Silver Contributor

        arturorosario 

         

        Thanks for that!

         

        In that script, on line 23 it is indeed using "%username%". That needs to go back to what you see in your screenshot, which is "$env:username".

         

        I've seen some whacky scripts in my day but this one is up there with the best of them!

         

        Still, as long as it works, who cares, right?

         

        And with that in mind, the drive mapping is taking place on line 196, and it is indeed taking place within PowerShell and not DOS, so this may be the only thing that needs to change to get your drive to work.

         

        So, what you're changing on line 23 is from this:

        $driveMappingJson = '[{"Path":"\\\\diskstation.musowls.org\\Students\\%username%","DriveLetter":"H","Label":"H_Drive","Id":1,"GroupFilter":null}]'

         

        To this:

        $driveMappingJson = '[{"Path":"\\\\diskstation.musowls.org\\Students\\$env:username","DriveLetter":"H","Label":"H_Drive","Id":1,"GroupFilter":null}]'

         

        Give that a go and let us know if it worked or if there's a new error to look at.

         

        Cheers,

        Lain

  • LainRobertson's avatar
    LainRobertson
    Silver Contributor

    arturorosario 

     

    The screenshot isn't overly-useful as we need to see more from line 25 downwards, to at least as far as where the drive mapping is being actioned.

     

    What doesn't add up in the screenshot is the reference to the %username% in the error. That's an environment variable from DOS not PowerShell. It makes sense that kind of reference won't work (at least not in a PowerShell session) which in turn validates the error message about "path not found", but what is less clear through not enough of the script being visible to us is how it got to that point.

     

    That fact that in the second iteration of the command you were able to map the drive successfully suggests (but doesn't guarantee) that permissions aren't the issue, despite the suggestion within the error text. You could verify that via klist but that's getting too far off the beaten track this early in the piece.

     

    If the home drive path is actually being pulled from something like the "homeDirectory" attribute from the on-premise Active Directory user account rather than the JSON (which is what we're guessing is the driver, but then that doesn't explain %username% cropping up in the error message) then you'd need to use a "-replace" PowerShell RegEx (or equivalent) to replace "%username%" with PowerShell's equivalent, "$env:username". But now I'm just wildly guessing because we can't see what we need to get ourselves informed.

     

    If you can paste more relevant parts of the script, we can probably be of more use than we are at the moment.

     

    Cheers,

    Lain

    • arturorosario's avatar
      arturorosario
      Copper Contributor
      Thanks so much for following up. Deploying the certificate was a success the drive is still not showing up using my login scripts. I'm researching how to implement "work drive" and how that works.

Resources