Repository: code-ready/crc
Pull request #1424 (2020-07-28 15:59:59)

Issue #1423 Add VPN note to `crc start` documentation
**Fixes:** Issue #1423

I used an admonition here to make the information stand out more than the rest of the prerequisites. Normally, this would be listed as another bullet item.

openshift-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1424#" title="Author self-approved">robin-owen</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=code-ready%2Fcrc).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[docs/OWNERS](https://github.com/code-ready/crc/blob/master/docs/OWNERS)~~ [robin-owen]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
openshift-ci-robot: @robin-owen: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 323150e281ccc7c8bebb09547a016c64fcbb13b1 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1424/pull-ci-code-ready-crc-master-e2e-crc/1288142170548604928) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1424). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:robin-owen).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/Makefile b/Makefile
index 5b550b127..dab4f2eec 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 SHELL := /bin/bash
 
-BUNDLE_VERSION = 4.5.1
+BUNDLE_VERSION = 4.5.4
 # OC_VERSION and BUNDLE_VERSION are going to same for release artifacts but
 # different for nightly and CI bits where bundle version would be any random
 # string or dd-mm-yyyy format.
diff --git a/centos_ci.sh b/centos_ci.sh
index 6b90fe0dd..baa535007 100644
--- a/centos_ci.sh
+++ b/centos_ci.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # bundle location
-BUNDLE_VERSION=4.5.1
+BUNDLE_VERSION=4.5.4
 BUNDLE=crc_libvirt_$BUNDLE_VERSION.crcbundle
 GO_VERSION=1.13.4
 
diff --git a/cmd/crc/cmd/client.go b/cmd/crc/cmd/client.go
new file mode 100644
index 000000000..c205a6236
--- /dev/null
+++ b/cmd/crc/cmd/client.go
@@ -0,0 +1,18 @@
+package cmd
+
+import "github.com/code-ready/crc/pkg/crc/machine"
+
+type client interface {
+	Status(statusConfig machine.ClusterStatusConfig) (machine.ClusterStatusResult, error)
+	Exists(name string) (bool, error)
+}
+
+type libmachineClient struct{}
+
+func (*libmachineClient) Status(statusConfig machine.ClusterStatusConfig) (machine.ClusterStatusResult, error) {
+	return machine.Status(statusConfig)
+}
+
+func (*libmachineClient) Exists(name string) (bool, error) {
+	return machine.Exists(name)
+}
diff --git a/cmd/crc/cmd/console.go b/cmd/crc/cmd/console.go
index b26aa8651..bca0d5fa7 100644
--- a/cmd/crc/cmd/console.go
+++ b/cmd/crc/cmd/console.go
@@ -41,7 +41,7 @@ func runConsole(arguments []string) error {
 		Name: constants.DefaultName,
 	}
 
-	if err := checkIfMachineMissing(consoleConfig.Name); err != nil {
+	if err := checkIfMachineMissing(&libmachineClient{}, consoleConfig.Name); err != nil {
 		return err
 	}
 
diff --git a/cmd/crc/cmd/delete.go b/cmd/crc/cmd/delete.go
index f9370e64c..7a850c7bb 100644
--- a/cmd/crc/cmd/delete.go
+++ b/cmd/crc/cmd/delete.go
@@ -40,7 +40,7 @@ func runDelete(arguments []string) error {
 		deleteCache()
 	}
 
-	if err := checkIfMachineMissing(deleteConfig.Name); err != nil {
+	if err := checkIfMachineMissing(&libmachineClient{}, deleteConfig.Name); err != nil {
 		return err
 	}
 
diff --git a/cmd/crc/cmd/ip.go b/cmd/crc/cmd/ip.go
index a4520bdeb..fed2fb0de 100644
--- a/cmd/crc/cmd/ip.go
+++ b/cmd/crc/cmd/ip.go
@@ -29,7 +29,7 @@ func runIP(arguments []string) error {
 		Debug: isDebugLog(),
 	}
 
-	if err := checkIfMachineMissing(ipConfig.Name); err != nil {
+	if err := checkIfMachineMissing(&libmachineClient{}, ipConfig.Name); err != nil {
 		return err
 	}
 
diff --git a/cmd/crc/cmd/podman_env.go b/cmd/crc/cmd/podman_env.go
index a94e60405..9610f7cde 100644
--- a/cmd/crc/cmd/podman_env.go
+++ b/cmd/crc/cmd/podman_env.go
@@ -36,7 +36,7 @@ func runPodmanEnv(args []string) error {
 		Debug: isDebugLog(),
 	}
 
-	if err := checkIfMachineMissing(ipConfig.Name); err != nil {
+	if err := checkIfMachineMissing(&libmachineClient{}, ipConfig.Name); err != nil {
 		return err
 	}
 
diff --git a/cmd/crc/cmd/root.go b/cmd/crc/cmd/root.go
index 1c7b49632..3c05bcd05 100644
--- a/cmd/crc/cmd/root.go
+++ b/cmd/crc/cmd/root.go
@@ -8,7 +8,6 @@ import (
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/exit"
 	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/code-ready/crc/pkg/crc/machine"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/crc/output"
 	"github.com/code-ready/crc/pkg/crc/preflight"
@@ -81,13 +80,13 @@ func setConfigDefaults() {
 	config.SetDefaults()
 }
 
-func checkIfMachineMissing(name string) error {
-	exists, err := machine.Exists(name)
+func checkIfMachineMissing(client client, name string) error {
+	exists, err := client.Exists(name)
 	if err != nil {
 		return err
 	}
 	if !exists {
-		return fmt.Errorf("Machine '%s' does not exist. Use 'crc start' to create it", constants.DefaultName)
+		return fmt.Errorf("Machine '%s' does not exist. Use 'crc start' to create it", name)
 	}
 	return nil
 }
diff --git a/cmd/crc/cmd/status.go b/cmd/crc/cmd/status.go
index 2f4430d05..f259d9534 100644
--- a/cmd/crc/cmd/status.go
+++ b/cmd/crc/cmd/status.go
@@ -1,10 +1,12 @@
 package cmd
 
 import (
+	"encoding/json"
 	"fmt"
+	"io"
 	"os"
 	"path/filepath"
-	"text/template"
+	"text/tabwriter"
 
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/exit"
@@ -13,7 +15,14 @@ import (
 	"github.com/spf13/cobra"
 )
 
+const jsonFormat = "json"
+
+var (
+	outputFormat string
+)
+
 func init() {
+	statusCmd.Flags().StringVarP(&outputFormat, "output", "o", "", "Output format. One of: json")
 	rootCmd.AddCommand(statusCmd)
 }
 
@@ -22,39 +31,33 @@ var statusCmd = &cobra.Command{
 	Short: "Display status of the OpenShift cluster",
 	Long:  "Show details about the OpenShift cluster",
 	Run: func(cmd *cobra.Command, args []string) {
-		if err := runStatus(args); err != nil {
+		if err := runStatus(os.Stdout, &libmachineClient{}, constants.MachineCacheDir, outputFormat); err != nil {
 			exit.WithMessage(1, err.Error())
 		}
 	},
 }
 
-var statusFormat = `CRC VM:          {{.CrcStatus}}
-OpenShift:       {{.OpenShiftStatus}}
-Disk Usage:      {{.DiskUsage}}
-Cache Usage:     {{.CacheUsage}}
-Cache Directory: {{.CacheDir}}
-`
-
 type Status struct {
-	CrcStatus       string
-	OpenShiftStatus string
-	DiskUsage       string
-	CacheUsage      string
-	CacheDir        string
+	CrcStatus        string `json:"crcStatus"`
+	OpenShiftStatus  string `json:"openshiftStatus"`
+	OpenShiftVersion string `json:"openshiftVersion"`
+	DiskUsage        int64  `json:"diskUsage"`
+	DiskSize         int64  `json:"diskSize"`
+	CacheUsage       int64  `json:"cacheUsage"`
+	CacheDir         string `json:"cacheDir"`
 }
 
-func runStatus(args []string) error {
+func runStatus(writer io.Writer, client client, cacheDir, outputFormat string) error {
 	statusConfig := machine.ClusterStatusConfig{Name: constants.DefaultName}
 
-	if err := checkIfMachineMissing(statusConfig.Name); err != nil {
+	if err := checkIfMachineMissing(client, statusConfig.Name); err != nil {
 		return err
 	}
 
-	clusterStatus, err := machine.Status(statusConfig)
+	clusterStatus, err := client.Status(statusConfig)
 	if err != nil {
 		return err
 	}
-	cacheDir := constants.MachineCacheDir
 	var size int64
 	err = filepath.Walk(cacheDir, func(_ string, info os.FileInfo, err error) error {
 		if !info.IsDir() {
@@ -65,22 +68,57 @@ func runStatus(args []string) error {
 	if err != nil {
 		return fmt.Errorf("Error finding size of cache: %s", err.Error())
 	}
-	cacheUsage := units.HumanSize(float64(size))
-	diskUse := units.HumanSize(float64(clusterStatus.DiskUse))
-	diskSize := units.HumanSize(float64(clusterStatus.DiskSize))
-	diskUsage := fmt.Sprintf("%s of %s (Inside the CRC VM)", diskUse, diskSize)
-	status := Status{clusterStatus.CrcStatus, clusterStatus.OpenshiftStatus, diskUsage, cacheUsage, cacheDir}
-	return printStatus(status, statusFormat)
+	status := Status{
+		CrcStatus:        clusterStatus.CrcStatus,
+		OpenShiftStatus:  clusterStatus.OpenshiftStatus,
+		OpenShiftVersion: clusterStatus.OpenshiftVersion,
+		DiskUsage:        clusterStatus.DiskUse,
+		DiskSize:         clusterStatus.DiskSize,
+		CacheUsage:       size,
+		CacheDir:         cacheDir,
+	}
+
+	if outputFormat == jsonFormat {
+		encoder := json.NewEncoder(writer)
+		encoder.SetIndent("", "  ")
+		return encoder.Encode(status)
+	}
+	return printStatus(writer, status)
 }
 
-func printStatus(status interface{}, statusFormat string) error {
-	tmpl, err := template.New("status").Parse(statusFormat)
-	if err != nil {
-		return fmt.Errorf("Error creating status template: %s", err.Error())
+func printStatus(writer io.Writer, status Status) error {
+	w := tabwriter.NewWriter(writer, 0, 0, 1, ' ', 0)
+
+	lines := []struct {
+		left, right string
+	}{
+		{"CRC VM", status.CrcStatus},
+		{"OpenShift", openshiftStatus(status)},
+		{"Disk Usage", fmt.Sprintf(
+			"%s of %s (Inside the CRC VM)",
+			units.HumanSize(float64(status.DiskUsage)),
+			units.HumanSize(float64(status.DiskSize)))},
+		{"Cache Usage", units.HumanSize(float64(status.CacheUsage))},
+		{"Cache Directory", status.CacheDir},
 	}
-	err = tmpl.Execute(os.Stdout, status)
-	if err != nil {
-		return fmt.Errorf("Error executing status template: %s", err.Error())
+	for _, line := range lines {
+		if err := printLine(w, line.left, line.right); err != nil {
+			return err
+		}
+	}
+	return w.Flush()
+}
+
+func openshiftStatus(status Status) string {
+	if status.OpenShiftVersion != "" {
+		return fmt.Sprintf("%s (v%s)", status.OpenShiftStatus, status.OpenShiftVersion)
+	}
+	return status.OpenShiftStatus
+}
+
+func printLine(w *tabwriter.Writer, left string, right string) error {
+	if _, err := fmt.Fprintf(w, "%s:\t%s\n", left, right); err != nil {
+		return err
 	}
 	return nil
 }
diff --git a/cmd/crc/cmd/stop.go b/cmd/crc/cmd/stop.go
index a311cfb40..ec046dd3d 100644
--- a/cmd/crc/cmd/stop.go
+++ b/cmd/crc/cmd/stop.go
@@ -36,7 +36,7 @@ func runStop(arguments []string) error {
 		Name: constants.DefaultName,
 	}
 
-	if err := checkIfMachineMissing(stopConfig.Name); err != nil {
+	if err := checkIfMachineMissing(&libmachineClient{}, stopConfig.Name); err != nil {
 		return err
 	}
 
diff --git a/docs/source/topics/proc_starting-the-virtual-machine.adoc b/docs/source/topics/proc_starting-the-virtual-machine.adoc
index 87615a76d..b2c55d1a7 100644
--- a/docs/source/topics/proc_starting-the-virtual-machine.adoc
+++ b/docs/source/topics/proc_starting-the-virtual-machine.adoc
@@ -5,6 +5,11 @@ The [command]`{bin} start` command starts the {prod} virtual machine and OpenShi
 
 .Prerequisites
 
+[NOTE]
+====
+To avoid networking-related issues, ensure that you are not connected to a VPN and that your network connection is reliable.
+====
+
 * You set up the host machine through the [command]`{bin} setup` command.
 For more information, see link:{crc-gsg-url}#setting-up-codeready-containers_gsg[Setting up {prod}].
 * You have a valid OpenShift user pull secret.
diff --git a/pkg/crc/machine/machine.go b/pkg/crc/machine/machine.go
index d6d085523..051b894dc 100644
--- a/pkg/crc/machine/machine.go
+++ b/pkg/crc/machine/machine.go
@@ -553,6 +553,7 @@ func Status(statusConfig ClusterStatusConfig) (ClusterStatusResult, error) {
 	}
 
 	openshiftStatus := "Stopped"
+	openshiftVersion := ""
 	var diskUse int64
 	var diskSize int64
 
@@ -586,11 +587,11 @@ func Status(statusConfig ClusterStatusConfig) (ClusterStatusResult, error) {
 		}
 		switch {
 		case operatorsStatus.Available:
-			openshiftVersion := "4.x"
+			openshiftVersion = "4.x"
 			if crcBundleMetadata.GetOpenshiftVersion() != "" {
 				openshiftVersion = crcBundleMetadata.GetOpenshiftVersion()
 			}
-			openshiftStatus = fmt.Sprintf("Running (v%s)", openshiftVersion)
+			openshiftStatus = "Running"
 		case operatorsStatus.Degraded:
 			openshiftStatus = "Degraded"
 		case operatorsStatus.Progressing:
@@ -602,12 +603,13 @@ func Status(statusConfig ClusterStatusConfig) (ClusterStatusResult, error) {
 		}
 	}
 	return ClusterStatusResult{
-		Name:            statusConfig.Name,
-		CrcStatus:       vmStatus.String(),
-		OpenshiftStatus: openshiftStatus,
-		DiskUse:         diskUse,
-		DiskSize:        diskSize,
-		Success:         true,
+		Name:             statusConfig.Name,
+		CrcStatus:        vmStatus.String(),
+		OpenshiftStatus:  openshiftStatus,
+		OpenshiftVersion: openshiftVersion,
+		DiskUse:          diskUse,
+		DiskSize:         diskSize,
+		Success:          true,
 	}, nil
 }
 
diff --git a/pkg/crc/machine/types.go b/pkg/crc/machine/types.go
index 5efd74291..a9b8234d7 100644
--- a/pkg/crc/machine/types.go
+++ b/pkg/crc/machine/types.go
@@ -92,13 +92,14 @@ type ClusterStatusConfig struct {
 }
 
 type ClusterStatusResult struct {
-	Name            string
-	CrcStatus       string
-	OpenshiftStatus string
-	DiskUse         int64
-	DiskSize        int64
-	Error           string
-	Success         bool
+	Name             string
+	CrcStatus        string
+	OpenshiftStatus  string
+	OpenshiftVersion string
+	DiskUse          int64
+	DiskSize         int64
+	Error            string
+	Success          bool
 }
 
 type ConsoleConfig struct {
diff --git a/pkg/crc/preflight/preflight_checks_tray_windows.go b/pkg/crc/preflight/preflight_checks_tray_windows.go
index 494151752..8bc481cff 100644
--- a/pkg/crc/preflight/preflight_checks_tray_windows.go
+++ b/pkg/crc/preflight/preflight_checks_tray_windows.go
@@ -4,95 +4,145 @@ import (
 	"fmt"
 	"io/ioutil"
 	goos "os"
-	"os/exec"
-	"os/user"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/input"
 	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/code-ready/crc/pkg/crc/version"
 	dl "github.com/code-ready/crc/pkg/download"
 	"github.com/code-ready/crc/pkg/embed"
 	"github.com/code-ready/crc/pkg/extract"
 	"github.com/code-ready/crc/pkg/os"
 	"github.com/code-ready/crc/pkg/os/windows/powershell"
-	"github.com/code-ready/crc/pkg/os/windows/secpol"
-	"github.com/code-ready/crc/pkg/os/windows/service"
 )
 
-var (
-	startUpFolder   = filepath.Join(constants.GetHomeDir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
-	trayProcessName = constants.TrayBinaryName[:len(constants.TrayBinaryName)-4]
-)
+func checkIfTrayInstalled() error {
+	/* We want to force installation whenever setup is ran
+	 * as we want the service config to point to the binary
+	 * with which the setup command was issued.
+	 */
 
-func checkIfDaemonServiceInstalled() error {
-	// We want to force installation whenever setup is ran
-	// as we want the service config to point to the binary
-	// with which the setup command was issued
-	return fmt.Errorf("Ignoring check and forcing installation of daemon service")
+	return fmt.Errorf("Ignoring check and forcing installation of System Tray")
 }
 
-func fixDaemonServiceInstalled() error {
-	// only try to remove if a previous version exists
-	if service.IsInstalled(constants.DaemonServiceName) {
-		if service.IsRunning(constants.DaemonServiceName) {
-			_ = service.Stop(constants.DaemonServiceName)
-		}
-		_ = service.Delete(constants.DaemonServiceName)
+func fixTrayInstalled() error {
+	/* To avoid asking for elevated privileges again and again
+	 * this takes care of all the steps needed to have a running
+	 * tray by invoking a ps script which does the following:
+	 * a) Add logon as service permission for the user
+	 * b) Create the daemon service and start it
+	 * c) Add tray to startup folder and start it
+	 */
+
+	tempDir, err := ioutil.TempDir("", "crc")
+	if err != nil {
+		logging.Error("Failed creating temporary directory for tray installation")
+		return err
 	}
 
-	// get executables path
+	defer func() {
+		_ = goos.RemoveAll(tempDir)
+	}()
+
+	// prepare the ps script
 	binPath, err := goos.Executable()
 	if err != nil {
 		return fmt.Errorf("Unable to find the current executables location: %v", err)
 	}
 	binPathWithArgs := fmt.Sprintf("%s daemon", strings.TrimSpace(binPath))
 
-	// get the account name
-	u, err := user.Current()
-	if err != nil {
-		return fmt.Errorf("Failed to get username: %v", err)
-	}
-	logging.Debug("Got username: ", u.Username)
-	accountName := strings.TrimSpace(u.Username)
-
 	// get the password from user
 	password, err := input.PromptUserForSecret("Enter account login password for service installation", "This is the login password of your current account, needed to install the daemon service")
 	if err != nil {
 		return fmt.Errorf("Unable to get login password: %v", err)
 	}
-	err = service.Create(constants.DaemonServiceName, binPathWithArgs, accountName, password)
+
+	// sanitize password
+	password = escapeWindowsPassword(password)
+
+	psScriptContent := genTrayInstallScript(
+		password,
+		tempDir,
+		binPathWithArgs,
+		constants.TrayBinaryPath,
+		constants.TrayShortcutName,
+		constants.DaemonServiceName,
+	)
+	psFilePath := filepath.Join(tempDir, "trayInstallation.ps1")
+
+	// write temporary ps script
+	if err = writePsScriptContentToFile(psScriptContent, psFilePath); err != nil {
+		return err
+	}
+
+	// invoke the ps script
+	_, _, err = powershell.ExecuteAsAdmin("Installing System Tray for CodeReady Containers", psFilePath)
+	// wait for the script to finish executing
+	time.Sleep(2 * time.Second)
 	if err != nil {
-		return fmt.Errorf("Failed to install CodeReady Containers daemon service: %v", err)
+		logging.Debug("Failed to execute tray installation script")
+		return err
+	}
+
+	// check for 'success' file
+	if _, err = goos.Stat(filepath.Join(tempDir, "success")); goos.IsNotExist(err) {
+		return fmt.Errorf("Installation script didn't execute successfully: %v", err)
 	}
 	return nil
 }
 
-func removeDaemonService() error {
-	// try to remove service if only it is installed
-	// this should remove unnecessary UAC prompts during cleanup
-	// service.IsInstalled doesn't need an admin shell to run
-	if service.IsInstalled(constants.DaemonServiceName) {
-		err := service.Stop(constants.DaemonServiceName)
-		if err != nil {
-			return fmt.Errorf("Failed to stop the daemon service: %v", err)
-		}
-		return service.Delete(constants.DaemonServiceName)
+func escapeWindowsPassword(password string) string {
+	// escape specials characters (|`|$|"|') with '`' if present in password
+	if strings.Contains(password, "`") {
+		password = strings.Replace(password, "`", "``", -1)
 	}
-	return nil
+	if strings.Contains(password, "$") {
+		password = strings.Replace(password, "$", "`$", -1)
+	}
+	if strings.Contains(password, "\"") {
+		password = strings.Replace(password, "\"", "`\"", -1)
+	}
+	if strings.Contains(password, "'") {
+		password = strings.Replace(password, "'", "`'", -1)
+	}
+	return password
 }
 
-func checkIfDaemonServiceRunning() error {
-	if service.IsRunning(constants.DaemonServiceName) {
+func removeTray() error {
+	trayProcessName := constants.TrayBinaryName[:len(constants.TrayBinaryName)-4]
+
+	tempDir, err := ioutil.TempDir("", "crc")
+	if err != nil {
+		logging.Debug("Failed to create temporary directory for System Tray removal")
+		return nil
+	}
+	defer func() {
+		_ = goos.RemoveAll(tempDir)
+	}()
+
+	psScriptContent := genTrayRemovalScript(
+		trayProcessName,
+		constants.TrayShortcutName,
+		constants.DaemonServiceName,
+		tempDir,
+	)
+	psFilePath := filepath.Join(tempDir, "trayRemoval.ps1")
+
+	// write script content to temporary file
+	if err = writePsScriptContentToFile(psScriptContent, psFilePath); err != nil {
+		logging.Debug(err)
 		return nil
 	}
-	return fmt.Errorf("CodeReady Containers dameon service is not running")
-}
 
-func fixDaemonServiceRunning() error {
-	return service.Start(constants.DaemonServiceName)
+	_, _, err = powershell.ExecuteAsAdmin("Uninstalling CodeReady Containers System Tray", psFilePath)
+	// wait for the script to finish executing
+	time.Sleep(2 * time.Second)
+	if err != nil {
+		logging.Debugf("Unable to execute System Tray uninstall script: %v", err)
+	}
+	return nil
 }
 
 func checkTrayBinaryExists() error {
@@ -131,123 +181,27 @@ func fixTrayBinaryExists() error {
 	return nil
 }
 
-func checkTrayBinaryVersion() error {
-	versionCmd := `(Get-Item %s).VersionInfo.ProductVersion`
-	stdOut, stdErr, err := powershell.Execute(fmt.Sprintf(versionCmd, constants.TrayBinaryPath))
-	if err != nil {
-		return fmt.Errorf("Failed to get the version of tray: %v: %s", err, stdErr)
-	}
-	currentTrayVersion := strings.TrimSpace(stdOut)
-	if currentTrayVersion != version.GetCRCWindowsTrayVersion() {
-		return fmt.Errorf("Current tray version doesn't match with expected version")
-	}
-	return nil
-}
-
-func fixTrayBinaryVersion() error {
-	// If a tray is already running kill it
-	if err := checkTrayRunning(); err == nil {
-		cmd := `Stop-Process -Name "tray-windows"`
-		if _, _, err := powershell.Execute(cmd); err != nil {
-			logging.Debugf("Failed to kill running tray: %v", err)
-		}
-	}
-	return fixTrayBinaryExists()
-}
-
-func checkTrayBinaryAddedToStartupFolder() error {
-	if os.FileExists(filepath.Join(startUpFolder, constants.TrayShortcutName)) {
-		return nil
-	}
-	return fmt.Errorf("Tray shortcut does not exists in startup folder")
-}
-
-func fixTrayBinaryAddedToStartupFolder() error {
-	cmd := fmt.Sprintf(
-		"New-Item -ItemType SymbolicLink -Path \"%s\" -Name \"%s\" -Value \"%s\"",
-		startUpFolder,
-		constants.TrayShortcutName,
-		constants.TrayBinaryPath,
-	)
-	_, _, err := powershell.ExecuteAsAdmin("Adding tray binary to startup applications", cmd)
-	if err != nil {
-		return fmt.Errorf("Failed to create shortcut of tray binary in startup folder: %v", err)
-	}
-	return nil
-}
-
-func removeTrayBinaryFromStartupFolder() error {
-	err := goos.Remove(filepath.Join(startUpFolder, constants.TrayShortcutName))
-	if err != nil {
-		logging.Warn("Failed to remove tray from startup folder: ", err)
-	}
-	return nil
-}
-
-func checkTrayRunning() error {
-	cmd := fmt.Sprintf("Get-Process -Name \"%s\"", trayProcessName)
-	_, stdErr, err := powershell.Execute(cmd)
-	if err != nil {
-		return fmt.Errorf("Failed to check if the tray is running: %v", err)
-	}
-	if strings.Contains(stdErr, constants.TrayBinaryName) {
-		return fmt.Errorf("Tray binary is not running")
-	}
-	return nil
-}
-
-func fixTrayRunning() error {
-	// #nosec G204
-	err := exec.Command(constants.TrayBinaryPath).Start()
+func writePsScriptContentToFile(psScriptContent, psFilePath string) error {
+	psFile, err := goos.Create(psFilePath)
 	if err != nil {
+		logging.Debugf("Unable to create file to write scipt content: %v", err)
 		return err
 	}
-	return nil
-}
-
-func stopTray() error {
-	if err := checkTrayRunning(); err != nil {
-		logging.Debug("Failed to check if a tray is running: ", err)
-		return nil
-	}
-	cmd := fmt.Sprintf("Stop-Process -Name %s", trayProcessName)
-	_, _, err := powershell.Execute(cmd)
+	defer psFile.Close()
+	// write the ps script
+	/* Add UTF-8 BOM at the beginning of the script so that Windows
+	 * correctly detects the file encoding
+	 */
+	_, err = psFile.Write([]byte{0xef, 0xbb, 0xbf})
 	if err != nil {
-		logging.Warn("Failed to stop running tray: ", err)
-	}
-	return nil
-}
-
-func checkUserHasServiceLogonEnabled() error {
-	username := getCurrentUsername()
-	enabled, err := secpol.UserAllowedToLogonAsService(username)
-	if enabled {
-		return nil
+		logging.Debugf("Unable to write script content to file: %v", err)
+		return err
 	}
-	return fmt.Errorf("Failed to check if user is allowed to log on as service: %v", err)
-}
-
-func fixUserHasServiceLogonEnabled() error {
-	// get the username
-	username := getCurrentUsername()
-
-	return secpol.AllowUserToLogonAsService(username)
-}
-
-func getCurrentUsername() string {
-	user, err := user.Current()
+	_, err = psFile.WriteString(psScriptContent)
 	if err != nil {
-		logging.Debugf("Unable to get current user's name: %v", err)
-		return ""
+		logging.Debugf("Unable to write script content to file: %v", err)
+		return err
 	}
-	return strings.TrimSpace(strings.Split(user.Username, "\\")[1])
-}
-
-func disableUserServiceLogon() error {
-	username := getCurrentUsername()
 
-	if err := secpol.RemoveLogonAsServiceUserRight(username); err != nil {
-		logging.Warn("Failed trying to remove log on as a service user right: ", err)
-	}
 	return nil
 }
diff --git a/pkg/crc/preflight/preflight_tray_powershell_windows.go b/pkg/crc/preflight/preflight_tray_powershell_windows.go
new file mode 100644
index 000000000..248b5f6c9
--- /dev/null
+++ b/pkg/crc/preflight/preflight_tray_powershell_windows.go
@@ -0,0 +1,170 @@
+package preflight
+
+import (
+	"fmt"
+	"strings"
+)
+
+var (
+	trayInstallationScript = []string{
+		`$ErrorActionPreference = "Stop"`,
+		`$password = "%s"`,
+		`$tempDir = "%s"`,
+		`$crcBinaryPath = "%s"`,
+		`$trayBinaryPath = "%s"`,
+		`$traySymlinkName = "%s"`,
+		`$serviceName = "%s"`,
+		`$currentUserSid = (Get-LocalUser -Name "$env:USERNAME").Sid.Value`,
+		`$startUpFolder = "$Env:USERPROFILE\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"`,
+		`function AddServiceLogonRightForCurrentUser()`,
+		`{`,
+		`	$securityTemplate = @"`,
+		`[Unicode]`,
+		`Unicode=yes`,
+		`[Version]`,
+		"signature=\"`$CHICAGO$\"",
+		`Revision=1`,
+		`[Privilege Rights]`,
+		`SeServiceLogonRight = {0}`,
+		`"@`,
+		`	SecEdit.exe /export /cfg $tempDir\secdef.inf /areas USER_RIGHTS`,
+		`	if ($LASTEXITCODE -ne 0)`,
+		`	{`,
+		`		exit 1`,
+		`	}`,
+		`	$userRights = Get-Content -Path $tempDir\secdef.inf`,
+		`	$serviceLogonUserRight = ($userRights | select-string -Pattern "SeServiceLogonRight\s=\s.*")`,
+		`	$sidsInServiceLogonRight = ($serviceLogonUserRight -split "=")[1].Trim()`,
+		`	$sidsArray = $sidsInServiceLogonRight -split ","`,
+		`	if (!($sidsArray.Contains($env:USERNAME) -or $sidsArray.Contains("*"+$currentUserSid)))`,
+		`	{`,
+		`		Write-Output "User doesn't have logon as service right, adding sid of $env:Username"`,
+		`		$sidsInServiceLogonRight += ",*$currentUserSid"`,
+		`		$templateContent = $securityTemplate -f "$sidsInServiceLogonRight"`,
+		`		Set-Content -Path $tempDir\secdef_fin.inf $templateContent`,
+		`		SecEdit.exe /configure /db $tempDir\tempdb.db /cfg $tempDir\secdef_fin.inf /areas USER_RIGHTS`,
+		`		if ($LASTEXITCODE -ne 0)`,
+		`		{`,
+		`			exit`,
+		`		}`,
+		`	}`,
+		`}`,
+
+		`function CreateDaemonService()`,
+		`{`,
+		`	$secPass = ConvertTo-SecureString $password -AsPlainText -Force`,
+		`	$creds = New-Object pscredential ("$env:USERDOMAIN\$env:USERNAME", $secPass)`,
+		`	$params = @{`,
+		`		Name = "$serviceName"`,
+		`		BinaryPathName = "$crcBinaryPath daemon"`,
+		`		DisplayName = "$serviceName"`,
+		`		StartupType = "Automatic"`,
+		`		Description = "CodeReady Containers Daemon service for System Tray."`,
+		`		Credential = $creds`,
+		`	}`,
+		`	New-Service @params`,
+		`}`,
+
+		`function StartDaemonService()`,
+		`{`,
+		`	Start-Service "CodeReady Containers"`,
+		`}`,
+
+		`AddServiceLogonRightForCurrentUser`,
+		`sc.exe stop "$serviceName"`,
+		`sc.exe delete "$serviceName"`,
+		`CreateDaemonService`,
+		`StartDaemonService`,
+		`$ErrorActionPreference = "Continue"`,
+		`Stop-Process -Name tray-windows`,
+		`Remove-Item "$startUpFolder\$traySymlinkName"`,
+
+		`$ErrorActionPreference = "Stop"`,
+		`New-Item -ItemType SymbolicLink -Path "$startUpFolder" -Name "$traySymlinkName" -Value "$trayBinaryPath"`,
+		`Start-Process -FilePath "$trayBinaryPath"`,
+		`New-Item -ItemType File -Path "$tempDir" -Name "success"`,
+		`Set-Content -Path $tempDir\success "blah blah"`,
+	}
+
+	trayRemovalScript = []string{
+		`$tempDir = "%s"`,
+		`$trayProcessName = "%s"`,
+		`$traySymlinkName = "%s"`,
+		`$serviceName = "%s"`,
+		`$startUpFolder = "$Env:USERPROFILE\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"`,
+
+		`function RemoveUserFromServiceLogon`,
+		`{`,
+		`	$securityTemplate = @"`,
+		`[Unicode]`,
+		`Unicode=yes`,
+		`[Version]`,
+		"signature=\"`$CHICAGO$\"",
+		`Revision=1`,
+		`[Privilege Rights]`,
+		`SeServiceLogonRight = {0}`,
+		`"@`,
+		`	SecEdit.exe /export /cfg $tempDir\secdef.inf /areas USER_RIGHTS`,
+		`	if ($LASTEXITCODE -ne 0)`,
+		`	{`,
+		`		exit 1`,
+		`	}`,
+
+		`	$userRights = Get-Content -Path $tempDir\secdef.inf`,
+		`	$serviceLogonUserRight = ($userRights | select-string -Pattern "SeServiceLogonRight\s=\s.*")`,
+
+		`	$sidsInServiceLogonRight = ($serviceLogonUserRight -split "=")[1].Trim()`,
+		`	$sidsArray = $sidsInServiceLogonRight -split ","`,
+		`	$newSids = $sidsArray | Where-Object {$_ -ne $env:USERNAME}`,
+		`	$newSids = $newSids -Join ","`,
+		`	$templateContent = $securityTemplate -f "$newSids"`,
+
+		`	Set-Content -Path $tempDir\secdef_fin.inf $templateContent`,
+		`	SecEdit.exe /configure /db $tempDir\tempdb.db /cfg $tempDir\secdef_fin.inf /areas USER_RIGHTS`,
+		`}`,
+
+		`function DeleteDaemonService()`,
+		`{`,
+		`	sc.exe stop "$serviceName"`,
+		`	sc.exe delete "$serviceName"`,
+		`}`,
+
+		`function RemoveTrayFromStartUpFolder()`,
+		`{`,
+		`	Stop-Process -Name "$trayProcessName"`,
+		`	Remove-Item "$startUpFolder\$traySymlinkName"`,
+		`}`,
+
+		`RemoveUserFromServiceLogon`,
+		`DeleteDaemonService`,
+		`RemoveTrayFromStartUpFolder`,
+	}
+)
+
+func getTrayInstallationScriptTemplate() string {
+	return strings.Join(trayInstallationScript, "\n")
+}
+
+func getTrayRemovalScriptTemplate() string {
+	return strings.Join(trayRemovalScript, "\n")
+}
+
+func genTrayInstallScript(password, tempDirPath, daemonCmd, trayBinaryPath, traySymlinkName, daemonServiceName string) string {
+	return fmt.Sprintf(getTrayInstallationScriptTemplate(),
+		password,
+		tempDirPath,
+		daemonCmd,
+		trayBinaryPath,
+		traySymlinkName,
+		daemonServiceName,
+	)
+}
+
+func genTrayRemovalScript(trayProcessName, traySymlinkName, daemonServiceName, tempDir string) string {
+	return fmt.Sprintf(getTrayRemovalScriptTemplate(),
+		tempDir,
+		trayProcessName,
+		traySymlinkName,
+		daemonServiceName,
+	)
+}
diff --git a/pkg/crc/preflight/preflight_windows.go b/pkg/crc/preflight/preflight_windows.go
index ddc4fe9ce..d8154ba45 100644
--- a/pkg/crc/preflight/preflight_windows.go
+++ b/pkg/crc/preflight/preflight_windows.go
@@ -56,31 +56,6 @@ var hypervPreflightChecks = [...]Check{
 }
 
 var traySetupChecks = [...]Check{
-	{
-		checkDescription:   "Checking if user is allowed to log on as a service",
-		check:              checkUserHasServiceLogonEnabled,
-		fixDescription:     "Enabling service log on for user",
-		fix:                fixUserHasServiceLogonEnabled,
-		cleanupDescription: "Removing service log on permission from user",
-		cleanup:            disableUserServiceLogon,
-		flags:              SetupOnly,
-	},
-	{
-		checkDescription:   "Checking if daemon service is installed",
-		check:              checkIfDaemonServiceInstalled,
-		fixDescription:     "Installing daemon service",
-		fix:                fixDaemonServiceInstalled,
-		cleanupDescription: "Removing daemon service if exists",
-		cleanup:            removeDaemonService,
-		flags:              SetupOnly,
-	},
-	{
-		checkDescription: "Checking if daemon service is running",
-		check:            checkIfDaemonServiceRunning,
-		fixDescription:   "Starting daemon service",
-		fix:              fixDaemonServiceRunning,
-		flags:            SetupOnly,
-	},
 	{
 		checkDescription: "Checking if tray binary is present",
 		check:            checkTrayBinaryExists,
@@ -89,28 +64,12 @@ var traySetupChecks = [...]Check{
 		flags:            SetupOnly,
 	},
 	{
-		checkDescription:   "Checking if tray binary is added to startup applications",
-		check:              checkTrayBinaryAddedToStartupFolder,
-		fixDescription:     "Adding tray binary to startup applications",
-		fix:                fixTrayBinaryAddedToStartupFolder,
-		cleanupDescription: "Removing tray binary from startup folder if exists",
-		cleanup:            removeTrayBinaryFromStartupFolder,
-		flags:              SetupOnly,
-	},
-	{
-		checkDescription: "Checking if tray version is correct",
-		check:            checkTrayBinaryVersion,
-		fixDescription:   "Caching correct tray version",
-		fix:              fixTrayBinaryVersion,
-		flags:            SetupOnly,
-	},
-	{
-		checkDescription:   "Checking if tray is running",
-		check:              checkTrayRunning,
-		fixDescription:     "Starting CodeReady Containers tray",
-		fix:                fixTrayRunning,
-		cleanupDescription: "Stopping tray process if running",
-		cleanup:            stopTray,
+		checkDescription:   "Checking if tray is installed",
+		check:              checkIfTrayInstalled,
+		fixDescription:     "Installing CodeReady Containers tray",
+		fix:                fixTrayInstalled,
+		cleanupDescription: "Uninstalling tray if installed",
+		cleanup:            removeTray,
 		flags:              SetupOnly,
 	},
 }
diff --git a/pkg/os/windows/secpol/secpol_windows.go b/pkg/os/windows/secpol/secpol_windows.go
deleted file mode 100644
index 2a24039c2..000000000
--- a/pkg/os/windows/secpol/secpol_windows.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package secpol
-
-import (
-	"fmt"
-	"io/ioutil"
-	goos "os"
-	"os/exec"
-	"os/user"
-	"path/filepath"
-	"regexp"
-	"strings"
-	"time"
-
-	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/code-ready/crc/pkg/os"
-	"github.com/code-ready/crc/pkg/os/windows/powershell"
-)
-
-const (
-	config = `[Unicode]
-Unicode=yes
-[Version]
-signature="$CHICAGO$"
-Revision=1
-[Privilege Rights]
-seservicelogonright = %s`
-)
-
-func getSidOfCurrentUser() (string, error) {
-	u, err := user.Current()
-	if err != nil {
-		return "", err
-	}
-	return u.Uid, nil
-}
-
-func getSeceditPath() string {
-	path, err := exec.LookPath("secedit.exe")
-	if err != nil {
-		logging.Debug("Cannot find 'secedit.exe' on path", err)
-		return ""
-	}
-	return path
-}
-
-// UserAllowedToLogonAsService returns true if the user's sid is present in 'seservicelogonright' config key
-func UserAllowedToLogonAsService(username string) (bool, error) {
-	/* To find out the user is allowed to log on as service by security policy
-	 * we export the User rights from the system security db at %windrive/defltbase.sdb using secedit.exe
-	 * to a temporary file and check if the given user's SID has the user right 'seservicelogonright'
-	 * see https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/security-policy-settings
-	 * and https://winaero.com/blog/reset-local-security-policy-settings-all-at-once-in-windows-10/
-	 */
-
-	logonUserRight := hasLogonUserRight(username)
-	if logonUserRight {
-		return true, nil
-	}
-
-	return false, nil
-}
-
-func hasLogonUserRight(username string) bool {
-	for _, sid := range getUsersWithLogonUserRights() {
-		/* seservicelogonright could either contain the username or the SID
-		 * the SID has a * at the beginning which needs to be removed to match with user's sid
-		 * if its a username it doesn't have the '*' in the beginning
-		 */
-		userSid, err := getSidOfCurrentUser()
-		if err != nil || sid == "" {
-			logging.Debug("Unable to get sid of user: ", err)
-		}
-		if sid[1:] == userSid || sid == username {
-			logging.Debug("Found user/sid in servicelogonright: ", sid)
-			return true
-		}
-	}
-	return false
-}
-
-// AllowUserToLogonAsService adds user's SID to 'seservicelogonright' config key in the security database
-func AllowUserToLogonAsService(username string) error {
-	userSid, err := getSidOfCurrentUser()
-	if err != nil {
-		return fmt.Errorf("Unable to get sid for username: %s: %v", username, err)
-	}
-
-	userLogonRightSids := getUsersWithLogonUserRights()
-	if len(userLogonRightSids) == 0 {
-		logging.Debugf("'seservicelogonright' config is empty or not present")
-	}
-
-	userSid = `*` + userSid
-
-	userLogonRightSids = append(userLogonRightSids, userSid)
-
-	seservicelogonrightConfig := fmt.Sprintf(config, strings.Join(userLogonRightSids, ","))
-
-	return configurePolicy(seservicelogonrightConfig)
-
-}
-
-func getUsersWithLogonUserRights() []string {
-	/* 'seservicelogon' key is a comma separated list of SIDs or usernames
-	 * the users present are allowed to log on as a  service
-	 * seservicelogonright = *S-1-5-21-3938078522-2608308634-3841203849-1001,*S-1-5-80-0
-	 */
-	seservicelogonright := getRawConfig("SeServiceLogonRight")
-	logging.Debug("Got raw config from file: ", seservicelogonright)
-	if len(seservicelogonright) == 0 {
-		return []string{}
-	}
-	var sids []string
-	s := strings.Split(seservicelogonright, "=")
-
-	if len(s) > 1 {
-		value := s[1]
-		value = strings.TrimSpace(value)
-		logging.Debug("Got users with SeServiceLogonRight: ", value)
-		// the values could be more than one or just one
-		// in case of just one there's no ',' and we cannot split on ','
-		if strings.Contains(value, ",") {
-			for _, s := range strings.Split(value, ",") {
-				if len(s) != 0 {
-					sids = append(sids, s)
-				}
-			}
-			return sids
-		}
-		// there's only one sid/user present so return just that
-		sids = append(sids, value)
-	}
-	return sids
-}
-
-func getRawConfig(configName string) string {
-	/* security configurations are stored in a inf file
-	 * each line contains a config key and a value delimited by an '=' sign
-	 * the value is a list of comma separated strings
-	 */
-
-	securityInfoFilePath, err := exportConfigFromSecurityDbToFile()
-	if err != nil {
-		logging.Debugf("Unable to get export config: %v", err)
-		return ""
-	}
-
-	defer func() {
-		_ = goos.RemoveAll(filepath.Dir(securityInfoFilePath))
-	}()
-
-	// open the security config file and get the line containing the config for configName
-	content, err := os.ReadFileUTF16LE(securityInfoFilePath)
-	if err != nil {
-		logging.Debugf("Unable to read for security info file: %v", err)
-		return ""
-	}
-
-	configPattern := configName + `\s=\s.*`
-	logging.Debug(configPattern)
-
-	re := regexp.MustCompile(configPattern)
-	match := re.Find(content)
-	if match == nil {
-		logging.Debugf("No match found, cannot find key %s in %s: %s", configName, securityInfoFilePath, string(content))
-		return ""
-	}
-
-	return string(match)
-}
-
-func exportConfigFromSecurityDbToFile() (string, error) {
-	// create temporary directory to export security info
-	// caller should remove this directory
-	path, err := ioutil.TempDir("", "crc")
-	if err != nil {
-		return "", err
-	}
-
-	securityInfoFilePath := filepath.Join(path, "sec.inf")
-	logging.Debug("Security info file path: ", securityInfoFilePath)
-
-	// export from security db at c:\windows\system32\defltbase.sdb
-	seceditExportCmd := fmt.Sprintf("%s /export /cfg %s /areas USER_RIGHTS", getSeceditPath(), securityInfoFilePath)
-	logging.Debug("Running command: ", seceditExportCmd)
-
-	_, stdErr, err := powershell.ExecuteAsAdmin("Running secedit export command", seceditExportCmd)
-	if stdErr != "" || err != nil {
-		return "", fmt.Errorf("%s: %v", stdErr, err)
-	}
-	// wait 2 seconds for command to finish executing
-	time.Sleep(2 * time.Second)
-	return securityInfoFilePath, nil
-}
-
-func configurePolicy(config string) error {
-	// create a config file to configure the policy using secedit
-	path, err := ioutil.TempDir("", "crc")
-	if err != nil {
-		return err
-	}
-	temporarySecurityDb := filepath.Join(path, "secdef.inf")
-	defer func() {
-		_ = goos.RemoveAll(path)
-	}()
-
-	securityInfoFilePath := filepath.Join(path, "sec.inf")
-	logging.Debug("Security info file path: ", securityInfoFilePath)
-	logging.Debug("Writing config: ", config)
-	err = ioutil.WriteFile(securityInfoFilePath, []byte(config), 0600)
-	if err != nil {
-		return err
-	}
-	// Write to security db at c:\windows\system32\defltbase.sdb
-	seceditConfigureCmd := fmt.Sprintf(
-		"%s /configure /db %s /cfg %s /areas USER_RIGHTS",
-		getSeceditPath(),
-		temporarySecurityDb,
-		securityInfoFilePath,
-	)
-	logging.Debugf("Running command: %s", seceditConfigureCmd)
-	_, stdErr, err := powershell.ExecuteAsAdmin("Running secedit configure command", seceditConfigureCmd)
-	if stdErr != "" || err != nil {
-		return fmt.Errorf("%s: %v", stdErr, err)
-	}
-	time.Sleep(2 * time.Second)
-	return nil
-}
-
-func RemoveLogonAsServiceUserRight(username string) error {
-	if !hasLogonUserRight(username) {
-		logging.Debugf("User does not have log on as a service right.")
-		return nil
-	}
-	userSid, err := getSidOfCurrentUser()
-	if err != nil {
-		return err
-	}
-
-	var sids []string
-	for _, s := range getUsersWithLogonUserRights() {
-		// skip the current user or it's SID
-		// the first character in SID is '*' need to skip it
-		if s[1:] == userSid || s == username {
-			logging.Debugf("Found current users sid: %s, will remove", s)
-			continue
-		}
-		sids = append(sids, s)
-	}
-	// write the new config
-	seservicelogonrightConfig := fmt.Sprintf(config, strings.Join(sids, ","))
-	return configurePolicy(seservicelogonrightConfig)
-}
diff --git a/pkg/os/windows/service/service_windows.go b/pkg/os/windows/service/service_windows.go
deleted file mode 100644
index 1ffa03bd2..000000000
--- a/pkg/os/windows/service/service_windows.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package service
-
-import (
-	"fmt"
-	"os/exec"
-	"strings"
-	"time"
-
-	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/code-ready/crc/pkg/os/windows/powershell"
-)
-
-func IsInstalled(serviceName string) bool {
-	cmd := fmt.Sprintf("Get-Service -Name \"%s\"", serviceName)
-	_, stdErr, err := powershell.Execute(cmd)
-	if err != nil {
-		logging.Debugf("Failed to execute powershell command: %v", err)
-		return false
-	}
-	if strings.Contains(stdErr, serviceName) {
-		return false
-	}
-	return true
-}
-
-func IsRunning(serviceName string) bool {
-	cmd := fmt.Sprintf("(Get-Service -Name \"%s\").Status", serviceName)
-	stdOut, _, err := powershell.Execute(cmd)
-	if err != nil {
-		logging.Debugf("Failed to execute powershell command: %v", err)
-		return false
-	}
-	if strings.TrimSpace(stdOut) == "Running" {
-		return true
-	}
-	return false
-}
-
-func Start(serviceName string) error {
-	cmd := fmt.Sprintf("%s start \"%s\"", getScExePath(), serviceName)
-	_, _, err := powershell.ExecuteAsAdmin(fmt.Sprintf("Starting service: %s", serviceName), cmd)
-	if err != nil {
-		logging.Debug("Failed to execute powershell command as admin")
-		return err
-	}
-	// Needs a bit of time to reflect the service status in svc manager
-	time.Sleep(2 * time.Second)
-
-	// Since ExecuteAsAdmin doesn't return useful stdOut or stdErr
-	// We independently check if the service was actually started
-	if IsRunning(serviceName) {
-		return nil
-	}
-	return fmt.Errorf("Could not start service: %s", serviceName)
-}
-
-func Stop(serviceName string) error {
-	cmd := fmt.Sprintf("%s stop \"%s\"", getScExePath(), serviceName)
-	_, _, err := powershell.ExecuteAsAdmin(fmt.Sprintf("Stopping service: %s", serviceName), cmd)
-	if err != nil {
-		logging.Debug("Failed to execute powershell command as admin")
-		return err
-	}
-	time.Sleep(2 * time.Second)
-	if IsRunning(serviceName) {
-		return fmt.Errorf("Could not stop service: %s", serviceName)
-	}
-	return nil
-}
-
-func Delete(serviceName string) error {
-	cmd := fmt.Sprintf("%s delete \"%s\"", getScExePath(), serviceName)
-	_, _, err := powershell.ExecuteAsAdmin(fmt.Sprintf("Uninstalling service: %s", serviceName), cmd)
-	if err != nil {
-		logging.Debug("Failed to execute powershell command as admin")
-		return err
-	}
-	time.Sleep(2 * time.Second)
-	if IsInstalled(serviceName) {
-		return fmt.Errorf("Could not uninstall service: %s", serviceName)
-	}
-	return nil
-}
-
-func Create(serviceName, binPath, accountName, password string) error {
-	var args = []string{
-		fmt.Sprintf("binPath= \"%s\"", binPath),
-		"type= own",
-		"start= auto",
-		fmt.Sprintf("DisplayName= \"%s\"", serviceName),
-		fmt.Sprintf("obj= \"%s\"", accountName),
-		fmt.Sprintf("password= %s", password),
-	}
-
-	cmd := fmt.Sprintf("%s create --%% \"%s\" %s", getScExePath(), serviceName, strings.Join(args, " "))
-	_, _, err := powershell.ExecuteAsAdmin("Install daemon service", cmd)
-	if err != nil {
-		logging.Debug("Failed to execute powershell command as admin")
-		return fmt.Errorf("Error installing service %s: %v", serviceName, err)
-	}
-	time.Sleep(2 * time.Second)
-	if !IsInstalled(serviceName) {
-		return fmt.Errorf("Failed to install service: %s", serviceName)
-	}
-	return nil
-}
-
-func getScExePath() string {
-	path, err := exec.LookPath("sc.exe")
-	if err != nil {
-		logging.Debug("Unable to find sc.exe on path")
-		return ""
-	}
-	return path
-}


Test patch:
diff --git a/cmd/crc/cmd/status_test.go b/cmd/crc/cmd/status_test.go
new file mode 100644
index 000000000..329830dcc
--- /dev/null
+++ b/cmd/crc/cmd/status_test.go
@@ -0,0 +1,74 @@
+package cmd
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestPlainStatus(t *testing.T) {
+	cacheDir, err := ioutil.TempDir("", "cache")
+	require.NoError(t, err)
+	defer os.RemoveAll(cacheDir)
+
+	require.NoError(t, ioutil.WriteFile(filepath.Join(cacheDir, "crc.qcow2"), make([]byte, 10000), 0600))
+
+	out := new(bytes.Buffer)
+	assert.NoError(t, runStatus(out, &mockClient{}, cacheDir, ""))
+
+	expected := `CRC VM:          Running
+OpenShift:       Running (v4.5.1)
+Disk Usage:      10GB of 20GB (Inside the CRC VM)
+Cache Usage:     10kB
+Cache Directory: %s
+`
+	assert.Equal(t, fmt.Sprintf(expected, cacheDir), out.String())
+}
+
+func TestJsonStatus(t *testing.T) {
+	cacheDir, err := ioutil.TempDir("", "cache")
+	require.NoError(t, err)
+	defer os.RemoveAll(cacheDir)
+
+	require.NoError(t, ioutil.WriteFile(filepath.Join(cacheDir, "crc.qcow2"), make([]byte, 10000), 0600))
+
+	out := new(bytes.Buffer)
+	assert.NoError(t, runStatus(out, &mockClient{}, cacheDir, jsonFormat))
+
+	expected := `{
+  "crcStatus": "Running",
+  "openshiftStatus": "Running",
+  "openshiftVersion": "4.5.1",
+  "diskUsage": 10000000000,
+  "diskSize": 20000000000,
+  "cacheUsage": 10000,
+  "cacheDir": "%s"
+}
+`
+	assert.Equal(t, fmt.Sprintf(expected, cacheDir), out.String())
+}
+
+type mockClient struct{}
+
+func (mockClient) Status(statusConfig machine.ClusterStatusConfig) (machine.ClusterStatusResult, error) {
+	return machine.ClusterStatusResult{
+		Name:             "crc",
+		CrcStatus:        "Running",
+		OpenshiftStatus:  "Running",
+		OpenshiftVersion: "4.5.1",
+		DiskUse:          10_000_000_000,
+		DiskSize:         20_000_000_000,
+		Success:          true,
+	}, nil
+}
+
+func (mockClient) Exists(name string) (bool, error) {
+	return true, nil
+}
diff --git a/pkg/crc/preflight/preflight_checks_tray_windows_test.go b/pkg/crc/preflight/preflight_checks_tray_windows_test.go
new file mode 100644
index 000000000..2f800383a
--- /dev/null
+++ b/pkg/crc/preflight/preflight_checks_tray_windows_test.go
@@ -0,0 +1,29 @@
+package preflight
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/code-ready/crc/pkg/os/windows/powershell"
+)
+
+func TestEscapeWindowsPassword(t *testing.T) {
+	passwords := []string{
+		"tes;t@\"_\\/",
+		"$kdhhjs;%&*'`",
+		"``````$''\"",
+	}
+
+	for _, pass := range passwords {
+		op := escapeWindowsPassword(pass)
+		psCmd := fmt.Sprintf("$var=\"%s\"; $var", op)
+		stdOut, stdErr, err := powershell.Execute(psCmd)
+		if err != nil {
+			t.Errorf("Error while executing powershell command: %v: %v", err, stdErr)
+		}
+		if strings.TrimSpace(stdOut) != pass {
+			t.Errorf("Passwords don't match after escaping for powershell. Expected: %s, Actual: %s", pass, stdOut)
+		}
+	}
+}