Skip to content

AzCopy should use %LOCALAPPDATA%\AzCopy on Windows, not %USERPROFILE%\.azcopy #3326

@daiplusplus

Description

@daiplusplus

Did you search for prior issues before submitting a new issue?

Yes:

Which version of the AzCopy was used?

Note: The version is visible when running AzCopy without any argument

v10.16.2

Which platform are you using? (ex: Windows, Mac, Linux)

Windows 10, Windows 11

What command did you run?

(Any command)

What problem was encountered?

  • AzCopy uses C:\Users\Me\.azcopy\ for its local-only data.
    • ...which gets copied around the network when Roaming Profiles are enabled. Not good.
  • Using the user-profile root means seldom-used directories start to pile-up in the navigation sidebar.
  • This behaviour violates Microsoft's own guidelines and requirements for software targeting Windows:
  • Finally, the comments in init_windows.go and platdiff_windows.go describe using "local appdata" even though this is presently not the case:

// GetAzCopyAppPath returns the path of Azcopy in local appdata.
func GetAzCopyAppPath() string {
userProfile := common.GetEnvironmentVariable(common.EEnvironmentVariable.UserDir())
azcopyAppDataFolder := path.Join(userProfile, ".azcopy")

// getAzCopyAppPath returns the path of Azcopy in local appdata.
func getAzCopyAppPath() string {
userProfile := GetEnvironmentVariable(EEnvironmentVariable.UserDir())
azcopyAppDataFolder := strings.ReplaceAll(path.Join(userProfile, ".azcopy"), "/", `\`)

How can we reproduce the problem in the simplest way?

Just use azcopy as normal.

Have you found a mitigation/solution?

Yes:

  • In common/environment.go add LocalDataDir() which uses LOCALAPPDATA on Windows:

    func (EnvironmentVariable) LocalDataDir() EnvironmentVariable {
    	// Only used internally, not listed in the environment variables.
    	return EnvironmentVariable{
    		Name: Iff(runtime.GOOS == "windows", "LOCALAPPDATA", "HOME"),
    	}
    }
  • In common/init_windows.go, use LocalDataDir instead of UserDir:

    // getAzCopyAppPath returns the path of Azcopy in local AppData. // e.g. C:\Users\billg\AppData\Local\AzCopy
    func getAzCopyAppPath() string {
    	localAppData := GetEnvironmentVariable(EEnvironmentVariable.LocalDataDir())
    	azcopyLocalAppDataFolder := strings.ReplaceAll(path.Join(localAppData , "AzCopy"), "/", `\`)
    
    	return azcopyLocalAppDataFolder
    }
    • However, it does make sense to continue using .azcopy if it's already in use, so an alternative approach might be:

      // getAzCopyAppPath returns the path of Azcopy in local AppData. // e.g. C:\Users\billg\AppData\Local\AzCopy
      func getAzCopyAppPath() string {
      	userProfile := GetEnvironmentVariable(EEnvironmentVariable.UserDir())
      	userProfileDotAzCopy := strings.ReplaceAll(path.Join(userProfile, ".azcopy"), "/", `\`)
      	if err := os.Mkdir(azcopyAppDataFolder, os.ModeDir); err != nil {
      		if os.IsExist(err) {
      			return userProfileDotAzCopy 
      		}
      	}
      
      	localAppData := GetEnvironmentVariable(EEnvironmentVariable.LocalDataDir())
      	azcopyLocalAppDataFolder := strings.ReplaceAll(path.Join(localAppData , "AzCopy"), "/", `\`)
      
      	return azcopyLocalAppDataFolder
      }
  • In platdiff_windows.go, similarly use LocalDataDir instead of UserDir:

    // GetAzCopyAppPath returns the path of AzCopy in local appdata // e.g. C:\Users\billg\AppData\Local\AzCopy
    func GetAzCopyAppPath() string {
    	localAppData := common.GetEnvironmentVariable(common.EEnvironmentVariable.LocalDataDir())
    	azcopyLocalAppDataFolder := path.Join(localAppData , "AzCopy")
    	if err := os.Mkdir(azcopyAppDataFolder, os.ModeDir); err != nil && !os.IsExist(err) {
    		return ""
    	}
    	return azcopyAppDataFolder
    }

I'm happy to submit this as a PR if everyone's happy with it.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions