/// <inheritdoc cref="DeployerBase"/> public void deploy() { var settings = this.GetSettings(); this.Deployment.windowsUsername = "******" + this.Deployment.installedApplicationSettings.GetId(); if (this.Deployment.GetPreviousDeployment() != null && this.Deployment.GetPreviousDeployment().windowsUsername != this.Deployment.windowsUsername) { this.Logger.LogWarning( false, "Windows account username has changed from '{0}' to '{1}', removal of account and granted permissions must be performed manually.", this.Deployment.GetPreviousDeployment()?.windowsUsername, this.Deployment.windowsUsername); } UtilsWindowsAccounts.EnsureUserExists(this.Deployment.WindowsUsernameFqdn(), this.Deployment.GetWindowsPassword(), this.Deployment.installedApplicationSettings.GetId(), this.Logger, this.GlobalSettings.directoryPrincipal); // Legacy behaviour, if no userGroups defined, create a chef_users groups and add the users // to it if (!(this.GlobalSettings.userGroups ?? new List <string>()).Any()) { UtilsWindowsAccounts.EnsureGroupExists(LEGACY_CHEF_USERS_GROUPNAME, this.GlobalSettings.directoryPrincipal); UtilsWindowsAccounts.EnsureUserInGroup(this.Deployment.WindowsUsernameFqdn(), LEGACY_CHEF_USERS_GROUPNAME, this.Logger, this.GlobalSettings.directoryPrincipal); } // Add the user to the user groups foreach (var groupIdentifier in this.GlobalSettings.userGroups ?? new List <string>()) { UtilsWindowsAccounts.EnsureUserInGroup(this.Deployment.WindowsUsernameFqdn(), groupIdentifier, this.Logger, this.GlobalSettings.directoryPrincipal); } // Add the user to any user groups defined at the application level foreach (var groupIdentifier in settings.user_groups ?? new List <string>()) { UtilsWindowsAccounts.EnsureUserInGroup(this.Deployment.WindowsUsernameFqdn(), groupIdentifier, this.Logger, this.GlobalSettings.directoryPrincipal); } // Add any privileges if requested foreach (var privilegeName in settings.privileges ?? new List <string>()) { UtilsWindowsAccounts.SetRight(this.Deployment.WindowsUsernameFqdn(), privilegeName, this.Logger); } // Getting security right at the OS level here is a little bit picky... // in order to have REALPATH to work in PHP we need to be able to read all directories // in a path i.e. D:\webs\chef\appnumber1\ // What we will do is disconnect the USERS account here... string basePath = UtilsSystem.CombinePaths(this.GlobalSettings.GetDefaultApplicationStorage().path, this.Deployment.getShortId()); UtilsSystem.EnsureDirectoryExists(basePath, true); UtilsWindowsAccounts.DisablePermissionInheritance(basePath); UtilsWindowsAccounts.RemoveAccessRulesForIdentity(new SecurityIdentifier(UtilsWindowsAccounts.WELL_KNOWN_SID_USERS), basePath, this.Logger); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), basePath, FileSystemRights.ReadAndExecute, this.GlobalSettings.directoryPrincipal); // Store this in the application storage location. this.Deployment.runtimePath = UtilsSystem.CombinePaths(basePath, "runtime"); UtilsSystem.EnsureDirectoryExists(this.Deployment.runtimePath, true); this.Deployment.runtimePathWritable = UtilsSystem.CombinePaths(basePath, "runtime_writable"); UtilsSystem.EnsureDirectoryExists(this.Deployment.runtimePathWritable, true); // Due to compatibility reasons with environments such as PHP (that do not play well with network file URIs such as shared folders) // by default these two directories are symlinked to a local path if they are network paths. // Temp dir string localTempPath = UtilsSystem.CombinePaths(this.Deployment.runtimePath, "temp"); string remoteTempPath = UtilsSystem.CombinePaths(this.GlobalSettings.GetDefaultTempStorage().path, this.Deployment.installedApplicationSettings.GetId()); UtilsSystem.EnsureDirectoryExists(remoteTempPath, true); UtilsJunction.EnsureLink(localTempPath, remoteTempPath, this.Logger, false); this.Deployment.tempPath = localTempPath; // Temp dir sys this.Deployment.tempPathSys = UtilsSystem.CombinePaths(this.Deployment.runtimePathWritable, "_tmp"); UtilsSystem.EnsureDirectoryExists(this.Deployment.tempPathSys, true); // Log dir string localLogPath = UtilsSystem.CombinePaths(this.Deployment.runtimePath, "log"); string remoteLogPath = UtilsSystem.CombinePaths(this.GlobalSettings.GetDefaultLogStorage().path, this.Deployment.installedApplicationSettings.GetId()); UtilsSystem.EnsureDirectoryExists(remoteLogPath, true); UtilsJunction.EnsureLink(localLogPath, remoteLogPath, this.Logger, false); this.Deployment.logPath = localLogPath; this.Deployment.SetSetting("appstorage.base", basePath); this.Deployment.SetSetting("appstorage.temp", this.Deployment.tempPath); this.Deployment.SetSetting("appstorage.log", this.Deployment.logPath); this.Deployment.SetSetting("appstorage.remote_temp", remoteTempPath); this.Deployment.SetSetting("appstorage.remote_log", remoteLogPath); // We use this flag to detect transient storage // that must be removed when the deployer is "undeployed". AppBaseStorageType appBaseStorageType = AppBaseStorageType.Original; // TODO: Make this configurable through the chef.yml settings file. string ignoreOnDeployPattern = "^\\.git\\\\|^chef\\\\|^\\.vs\\\\"; switch (this.Deployment.installedApplicationSettings.GetApplicationMountStrategy()) { case ApplicationMountStrategy.Copy: this.Deployment.appPath = UtilsSystem.CombinePaths(basePath, "app"); // TODO: We should consider the ability to symlink the code here, or to point/mount directly // to the original source path. This would probably require delegating this step to the artifact downloader // (artifact.getDownloader()) or having the downloader tell us how to deal with this (symlinks, direct, whatever) this.Logger.LogInfo(true, "Copying artifact files..."); UtilsSystem.CopyFilesRecursivelyFast(this.Deployment.artifact.localPath, this.Deployment.appPath, false, ignoreOnDeployPattern, this.Logger); this.Logger.LogInfo(true, "Ensure app has proper user permissions for account '{0}'", this.Deployment.WindowsUsernameFqdn()); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUserPrincipalName(), this.Deployment.appPath, FileSystemRights.ReadAndExecute, this.GlobalSettings.directoryPrincipal); this.Deployment.artifact.DeleteIfRemote(this.Logger); appBaseStorageType = AppBaseStorageType.Transient; break; case ApplicationMountStrategy.Move: this.Deployment.appPath = UtilsSystem.CombinePaths(basePath, "app"); // TODO: We should consider the ability to symlink the code here, or to point/mount directly // to the original source path. This would probably require delegating this step to the artifact downloader // (artifact.getDownloader()) or having the downloader tell us how to deal with this (symlinks, direct, whatever) this.Logger.LogInfo(true, "Moving artifact files..."); UtilsSystem.MoveDirectory(this.Deployment.artifact.localPath, this.Deployment.appPath, this.Logger, ignoreOnDeployPattern); // We had issues in appveyor where _webs location is in C drive and thus not giving // permissions here would make tests fail. this.Logger.LogInfo(true, "Ensure app has proper user permissions for account '{0}'", this.Deployment.WindowsUsernameFqdn()); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), this.Deployment.appPath, FileSystemRights.ReadAndExecute, this.GlobalSettings.directoryPrincipal); this.Deployment.artifact.DeleteIfRemote(this.Logger); appBaseStorageType = AppBaseStorageType.Transient; break; case ApplicationMountStrategy.Link: this.Logger.LogInfo(true, "Linking artifact files..."); this.Deployment.appPath = UtilsSystem.CombinePaths(basePath, "app"); UtilsJunction.EnsureLink(this.Deployment.appPath, this.Deployment.artifact.localPath, this.Logger, false); this.Logger.LogInfo(true, "Ensure app has proper user permissions for account '{0}'", this.Deployment.WindowsUsernameFqdn()); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), this.Deployment.artifact.localPath, FileSystemRights.ReadAndExecute, this.GlobalSettings.directoryPrincipal); appBaseStorageType = AppBaseStorageType.Symlink; break; case ApplicationMountStrategy.Original: this.Logger.LogInfo(true, "Ensure app has proper user permissions for account '{0}'", this.Deployment.WindowsUsernameFqdn()); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), this.Deployment.artifact.localPath, FileSystemRights.ReadAndExecute, this.GlobalSettings.directoryPrincipal); this.Deployment.appPath = UtilsSystem.CombinePaths(this.Deployment.artifact.localPath); appBaseStorageType = AppBaseStorageType.Original; break; default: throw new NotImplementedException("The requested mount strategy for the application is not available: " + this.Deployment.installedApplicationSettings.GetApplicationMountStrategy()); } this.Deployment.SetRuntimeSetting("deployment.appPath", this.Deployment.appPath); this.Deployment.SetRuntimeSetting("deployment.logPath", this.Deployment.logPath); this.Deployment.SetRuntimeSetting("deployment.tempPath", this.Deployment.tempPath); this.Deployment.SetSetting("appstorage.appBaseStorageType", appBaseStorageType); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), remoteTempPath, FileSystemRights.Write | FileSystemRights.Read | FileSystemRights.Delete, this.GlobalSettings.directoryPrincipal); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), remoteLogPath, FileSystemRights.Write | FileSystemRights.Read | FileSystemRights.Delete, this.GlobalSettings.directoryPrincipal); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), this.Deployment.runtimePath, FileSystemRights.ReadAndExecute, this.GlobalSettings.directoryPrincipal); UtilsWindowsAccounts.AddPermissionToDirectoryIfMissing(this.Deployment.WindowsUsernameFqdn(), this.Deployment.runtimePathWritable, FileSystemRights.Write | FileSystemRights.Read | FileSystemRights.Delete, this.GlobalSettings.directoryPrincipal); this.DeployFonts(settings); }