agent/setupcli/managers/packagemanagers/windowsmanager.go (241 lines of code) (raw):
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//go:build windows
// +build windows
// Package packagemanagers holds functions querying using local package manager
package packagemanagers
import (
"fmt"
"io/ioutil"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/aws/amazon-ssm-agent/agent/appconfig"
"github.com/aws/amazon-ssm-agent/agent/fileutil"
"github.com/aws/amazon-ssm-agent/agent/log"
"github.com/aws/amazon-ssm-agent/agent/platform"
"github.com/aws/amazon-ssm-agent/agent/setupcli/managers/common"
"github.com/aws/amazon-ssm-agent/agent/setupcli/managers/servicemanagers"
"github.com/aws/amazon-ssm-agent/agent/setupcli/managers/verificationmanagers"
"github.com/aws/amazon-ssm-agent/agent/setupcli/utility"
"github.com/aws/amazon-ssm-agent/agent/updateutil/updateconstants"
"golang.org/x/sys/windows/registry"
)
type windowsManager struct {
managerHelper common.IManagerHelper
}
var (
RegistryNotExistErr = fmt.Errorf("%v", "RegistryNotExistErrCode")
FailedToOpenRegistryErr = fmt.Errorf("%v", "FailedToOpenRegistryErr")
isNanoServer = platform.IsPlatformNanoServer
timeSleep = time.Sleep
fileUnCompress = fileutil.Uncompress
fileMoveFiles = fileutil.MoveFiles
ioUtilReadDir = ioutil.ReadDir
deleteDir = fileutil.DeleteDirectory
getInstalledAgentVersionFunc = getInstalledAgentVersion
nanoPackageZip = "package.zip"
)
// GetFilesReqForInstall returns all the files the package manager needs to install the agent
func (m *windowsManager) GetFilesReqForInstall(log log.T) []string {
if isNano, _ := isNanoServer(log); isNano {
return []string{
nanoPackageZip,
}
}
return []string{
common.AmazonWindowsSetupFile,
}
}
// InstallAgent installs the agent using package manager, folderPath should contain all files required for installation
func (m *windowsManager) InstallAgent(log log.T, installedAgentVersionPath string) (err error) {
isNano, _ := isNanoServer(log)
if isNano {
log.Infof("Windows Nano platform detected during install")
}
if isNano {
err = m.installOnWindowsNano(log, installedAgentVersionPath)
} else {
err = m.installOnWindows(installedAgentVersionPath)
}
if err == nil {
timeSleep(5 * time.Second)
return
}
timeSleep(5 * time.Second)
if err != nil {
installedAgentVersion, _ := m.GetInstalledAgentVersion()
if !strings.Contains(installedAgentVersionPath, installedAgentVersion) {
return err
}
}
return
}
// UninstallAgent uninstalls the agent using the package manager
func (m *windowsManager) UninstallAgent(log log.T, installedAgentVersionPath string) (err error) {
isNano, _ := isNanoServer(log)
if isNano {
log.Infof("Windows Nano platform detected")
}
if isNano {
err = m.uninstallOnWindowsNano(log, installedAgentVersionPath)
return
}
err = m.uninstallOnWindows(installedAgentVersionPath)
timeSleep(2 * time.Second)
return
}
// IsAgentInstalled returns true if agent is installed using package manager, returns error for any unexpected errors
func (m *windowsManager) IsAgentInstalled() (bool, error) {
_, err := getInstalledAgentVersionFunc(m)
if err == nil {
return true, nil
}
if strings.HasPrefix(err.Error(), FailedToOpenRegistryErr.Error()) {
return false, fmt.Errorf("failed to open registry for agent to determine if agent is installed")
}
return !strings.HasPrefix(err.Error(), RegistryNotExistErr.Error()), nil
}
// IsManagerEnvironment returns true if all commands required by the package manager are available
func (m *windowsManager) IsManagerEnvironment() bool {
return runtime.GOOS == "windows"
}
// GetSupportedServiceManagers returns all the service manager types that the package manager supports
func (m *windowsManager) GetSupportedServiceManagers() []servicemanagers.ServiceManager {
return []servicemanagers.ServiceManager{servicemanagers.Windows}
}
// GetName returns the package manager name
func (m *windowsManager) GetName() string {
return "windows"
}
// GetType returns the package manager type
func (m *windowsManager) GetType() PackageManager {
return Windows
}
// GetInstalledAgentVersion returns the version of the installed agent
func (m *windowsManager) GetInstalledAgentVersion() (string, error) {
return getInstalledAgentVersionFunc(m)
}
func getInstalledAgentVersion(m *windowsManager) (string, error) {
ssmKey, err := registry.OpenKey(registry.LOCAL_MACHINE, appconfig.ItemPropertyPath, registry.QUERY_VALUE)
if err != nil {
if err == registry.ErrNotExist {
return "", RegistryNotExistErr
} else {
return "", FailedToOpenRegistryErr
}
}
defer ssmKey.Close()
version, _, err := ssmKey.GetStringValue("Version")
if err != nil {
if err == registry.ErrNotExist {
output, err := m.managerHelper.RunCommand(appconfig.DefaultSSMAgentBinaryPath, "--version")
output = utility.CleanupVersion(output)
if output != "" {
return output, nil
}
return "", fmt.Errorf("failed to get agent version from registry as well as agent binary: %v", err)
} else {
return "", fmt.Errorf("failed to open agent version attribute from registry: %v", err)
}
}
return utility.CleanupVersion(version), nil
}
// GetFileExtension returns the file extension of the agent using the package manager
func (m *windowsManager) GetFileExtension() string {
return ".exe"
}
// GetSupportedVerificationManager returns verification manager types that the package manager supports
func (m *windowsManager) GetSupportedVerificationManager() verificationmanagers.VerificationManager {
return verificationmanagers.Windows
}
func (m *windowsManager) installOnWindowsNano(log log.T, installedAgentVersionPath string) error {
tempFolder := "temp"
scExePath := "sc.exe"
tempPath := filepath.Join(installedAgentVersionPath, tempFolder)
srcZipFile := filepath.Join(installedAgentVersionPath, nanoPackageZip)
err := fileUnCompress(log, srcZipFile, tempPath)
if err != nil {
return fmt.Errorf("uncompress of nano packages failed: %v", err)
}
err = m.uninstallOnWindowsNano(log, installedAgentVersionPath)
if err != nil {
return err
}
err = fileMoveFiles(tempPath, appconfig.DefaultProgramFolder)
if err != nil {
return fmt.Errorf("moving files to program folder failed: %v", err)
}
amazonExecutable := filepath.Join(appconfig.DefaultProgramFolder, common.AmazonSSMExecutable)
_, err = m.managerHelper.RunCommand(scExePath, "create", "AmazonSSMAgent", "binpath='"+amazonExecutable+"'", "start=auto", "displayname='Amazon SSM Agent'")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows scCreate install: command timed out")
}
return fmt.Errorf("windows scCreate install: failed to install with error: %v", err)
}
_, err = m.managerHelper.RunCommand(scExePath, "description", "AmazonSSMAgent", "'Amazon SSM Agent'")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows description update: command timed out")
}
return fmt.Errorf("windows description update: failed to install with error: %v", err)
}
_, err = m.managerHelper.RunCommand(scExePath, "failureflag", "AmazonSSMAgent", "1")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows service failure flag update: Command timed out")
}
return fmt.Errorf("windows service failure flag update: Failed to install with error: %v", err)
}
_, err = m.managerHelper.RunCommand(scExePath, "failure", "AmazonSSMAgent", "reset=86400", "actions= restart/30000/restart/30000/restart/30000")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows restart service: Command timed out")
}
return fmt.Errorf("windows restart service: Failed to install with error: %v", err)
}
netExecPath := "C:\\Windows\\System32\\net.exe"
_, err = m.managerHelper.RunCommand(netExecPath, "start", "AmazonSSMAgent")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows start: Command timed out")
}
return fmt.Errorf("windows start: Failed to install with error: %v", err)
}
return nil
}
func (m *windowsManager) uninstallOnWindowsNano(log log.T, installedAgentVersionPath string) error {
netExecPath := "C:\\Windows\\System32\\net.exe"
_, err := m.managerHelper.RunCommand(netExecPath, "stop", "AmazonSSMAgent")
if err != nil {
log.Warnf("error while agent stop: %v", err)
}
_, err = m.managerHelper.RunCommand("Get-CimInstance", "-ClassName", "Win32_Service", "-Filter", "'Name=\"AmazonSSMAgent\"'", "|", "Invoke-CimMethod", "-MethodName", "Delete")
if err != nil {
log.Warnf("error while querying agent info: %v", err)
}
files := make([]string, 0)
skipFiles := map[string]interface{}{appconfig.SeelogConfigFileName: struct{}{}, appconfig.AppConfigFileName: struct{}{}}
if list, err := ioUtilReadDir(appconfig.DefaultProgramFolder); err == nil {
for _, fileInfo := range list {
filePath := filepath.Join(appconfig.DefaultProgramFolder, fileInfo.Name())
files = append(files, filePath)
if _, ok := skipFiles[fileInfo.Name()]; ok {
continue
}
err = deleteDir(filePath)
if err != nil {
log.Warnf("error while deleting directory during uninstall: %v", err)
}
}
}
return nil
}
func (m *windowsManager) installOnWindows(installedAgentVersionPath string) error {
setupExecPath := filepath.Join(installedAgentVersionPath, common.AmazonWindowsSetupFile)
output, err := m.managerHelper.RunCommandWithCustomTimeout(time.Duration(updateconstants.DefaultUpdateExecutionTimeoutInSeconds)*time.Second, appconfig.PowerShellPluginCommandName, "(Start-Process", "'"+setupExecPath+"'", "-ArgumentList @('ALLOWEC2INSTALL=YES', 'SKIPSYMLINKSCAN=YES', '/quiet', '/norestart')", "-Wait)")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows install: Command timed out")
}
return fmt.Errorf("windows install: Failed to install with error %v: %v", setupExecPath, output)
}
return nil
}
func (m *windowsManager) uninstallOnWindows(installedAgentVersionPath string) error {
setupExecPath := filepath.Join(installedAgentVersionPath, common.AmazonWindowsSetupFile)
output, err := m.managerHelper.RunCommandWithCustomTimeout(time.Duration(updateconstants.DefaultUpdateExecutionTimeoutInSeconds)*time.Second, appconfig.PowerShellPluginCommandName, "(Start-Process", "'"+setupExecPath+"'", "-ArgumentList @('/uninstall', '/quiet', '/norestart')", "-Wait)")
if err != nil {
if m.managerHelper.IsTimeoutError(err) {
return fmt.Errorf("windows uninstall: Command timed out")
}
return fmt.Errorf("windows uninstall: Failed to uninstall with error: %v", output)
}
return nil
}