2015-04-10 20:34:46 +02:00
|
|
|
package winrm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2015-04-30 18:02:33 +02:00
|
|
|
"path/filepath"
|
2015-04-10 20:34:46 +02:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2016-09-04 00:45:24 +02:00
|
|
|
"github.com/hashicorp/terraform/communicator/shared"
|
2020-11-25 19:19:27 +01:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
2021-05-10 19:57:11 +02:00
|
|
|
"github.com/zclconf/go-cty/cty/gocty"
|
2015-04-10 20:34:46 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DefaultUser is used if there is no user given
|
|
|
|
DefaultUser = "Administrator"
|
|
|
|
|
|
|
|
// DefaultPort is used if there is no port given
|
|
|
|
DefaultPort = 5985
|
|
|
|
|
2018-12-13 00:19:02 +01:00
|
|
|
// DefaultHTTPSPort is used if there is no port given and HTTPS is true
|
|
|
|
DefaultHTTPSPort = 5986
|
|
|
|
|
2015-04-10 20:34:46 +02:00
|
|
|
// DefaultScriptPath is used as the path to copy the file to
|
|
|
|
// for remote execution if not provided otherwise.
|
2015-04-30 18:02:33 +02:00
|
|
|
DefaultScriptPath = "C:/Temp/terraform_%RAND%.cmd"
|
2015-04-10 20:34:46 +02:00
|
|
|
|
|
|
|
// DefaultTimeout is used if there is no timeout given
|
|
|
|
DefaultTimeout = 5 * time.Minute
|
|
|
|
)
|
|
|
|
|
|
|
|
// connectionInfo is decoded from the ConnInfo of the resource. These are the
|
|
|
|
// only keys we look at. If a KeyFile is given, that is used instead
|
|
|
|
// of a password.
|
|
|
|
type connectionInfo struct {
|
|
|
|
User string
|
|
|
|
Password string
|
|
|
|
Host string
|
2021-05-10 19:57:11 +02:00
|
|
|
Port uint16
|
2015-04-10 20:34:46 +02:00
|
|
|
HTTPS bool
|
|
|
|
Insecure bool
|
2018-03-31 03:11:53 +02:00
|
|
|
NTLM bool `mapstructure:"use_ntlm"`
|
2017-10-23 22:28:41 +02:00
|
|
|
CACert string `mapstructure:"cacert"`
|
2015-04-10 20:34:46 +02:00
|
|
|
Timeout string
|
|
|
|
ScriptPath string `mapstructure:"script_path"`
|
|
|
|
TimeoutVal time.Duration `mapstructure:"-"`
|
|
|
|
}
|
|
|
|
|
2020-11-25 19:19:27 +01:00
|
|
|
// decodeConnInfo decodes the given cty.Value using the same behavior as the
|
|
|
|
// lgeacy mapstructure decoder in order to preserve as much of the existing
|
|
|
|
// logic as possible for compatibility.
|
|
|
|
func decodeConnInfo(v cty.Value) (*connectionInfo, error) {
|
2015-04-10 20:34:46 +02:00
|
|
|
connInfo := &connectionInfo{}
|
2020-11-25 19:19:27 +01:00
|
|
|
if v.IsNull() {
|
|
|
|
return connInfo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range v.AsValueMap() {
|
|
|
|
if v.IsNull() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch k {
|
|
|
|
case "user":
|
|
|
|
connInfo.User = v.AsString()
|
|
|
|
case "password":
|
|
|
|
connInfo.Password = v.AsString()
|
|
|
|
case "host":
|
|
|
|
connInfo.Host = v.AsString()
|
|
|
|
case "port":
|
2021-05-10 19:57:11 +02:00
|
|
|
if err := gocty.FromCtyValue(v, &connInfo.Port); err != nil {
|
2020-11-25 19:19:27 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
case "https":
|
|
|
|
connInfo.HTTPS = v.True()
|
|
|
|
case "insecure":
|
|
|
|
connInfo.Insecure = v.True()
|
|
|
|
case "use_ntlm":
|
|
|
|
connInfo.NTLM = v.True()
|
|
|
|
case "cacert":
|
|
|
|
connInfo.CACert = v.AsString()
|
|
|
|
case "script_path":
|
|
|
|
connInfo.ScriptPath = v.AsString()
|
|
|
|
case "timeout":
|
|
|
|
connInfo.Timeout = v.AsString()
|
|
|
|
}
|
2015-04-10 20:34:46 +02:00
|
|
|
}
|
2020-11-25 19:19:27 +01:00
|
|
|
return connInfo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseConnectionInfo is used to convert the ConnInfo of the InstanceState into
|
|
|
|
// a ConnectionInfo struct
|
|
|
|
func parseConnectionInfo(v cty.Value) (*connectionInfo, error) {
|
|
|
|
v, err := shared.ConnectionBlockSupersetSchema.CoerceValue(v)
|
2015-04-10 20:34:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-11-25 19:19:27 +01:00
|
|
|
|
|
|
|
connInfo, err := decodeConnInfo(v)
|
|
|
|
if err != nil {
|
2015-04-10 20:34:46 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-04-30 18:02:33 +02:00
|
|
|
// Check on script paths which point to the default Windows TEMP folder because files
|
|
|
|
// which are put in there very early in the boot process could get cleaned/deleted
|
|
|
|
// before you had the change to execute them.
|
|
|
|
//
|
|
|
|
// TODO (SvH) Needs some more debugging to fully understand the exact sequence of events
|
|
|
|
// causing this...
|
|
|
|
if strings.HasPrefix(filepath.ToSlash(connInfo.ScriptPath), "C:/Windows/Temp") {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
`Using the C:\Windows\Temp folder is not supported. Please use a different 'script_path'.`)
|
|
|
|
}
|
|
|
|
|
2015-04-10 20:34:46 +02:00
|
|
|
if connInfo.User == "" {
|
|
|
|
connInfo.User = DefaultUser
|
|
|
|
}
|
2016-09-04 00:45:24 +02:00
|
|
|
|
|
|
|
// Format the host if needed.
|
|
|
|
// Needed for IPv6 support.
|
|
|
|
connInfo.Host = shared.IpFormat(connInfo.Host)
|
|
|
|
|
2015-04-10 20:34:46 +02:00
|
|
|
if connInfo.Port == 0 {
|
2018-12-13 00:19:02 +01:00
|
|
|
if connInfo.HTTPS {
|
|
|
|
connInfo.Port = DefaultHTTPSPort
|
|
|
|
} else {
|
|
|
|
connInfo.Port = DefaultPort
|
|
|
|
}
|
2015-04-10 20:34:46 +02:00
|
|
|
}
|
2015-04-30 18:02:33 +02:00
|
|
|
if connInfo.ScriptPath == "" {
|
2015-04-10 20:34:46 +02:00
|
|
|
connInfo.ScriptPath = DefaultScriptPath
|
|
|
|
}
|
|
|
|
if connInfo.Timeout != "" {
|
|
|
|
connInfo.TimeoutVal = safeDuration(connInfo.Timeout, DefaultTimeout)
|
|
|
|
} else {
|
|
|
|
connInfo.TimeoutVal = DefaultTimeout
|
|
|
|
}
|
2015-04-30 18:02:33 +02:00
|
|
|
|
2015-04-10 20:34:46 +02:00
|
|
|
return connInfo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// safeDuration returns either the parsed duration or a default value
|
|
|
|
func safeDuration(dur string, defaultDur time.Duration) time.Duration {
|
|
|
|
d, err := time.ParseDuration(dur)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Invalid duration '%s', using default of %s", dur, defaultDur)
|
|
|
|
return defaultDur
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatDuration(duration time.Duration) string {
|
|
|
|
h := int(duration.Hours())
|
2015-10-08 14:48:04 +02:00
|
|
|
m := int(duration.Minutes()) - h*60
|
2015-04-10 20:34:46 +02:00
|
|
|
s := int(duration.Seconds()) - (h*3600 + m*60)
|
|
|
|
|
|
|
|
res := "PT"
|
|
|
|
if h > 0 {
|
|
|
|
res = fmt.Sprintf("%s%dH", res, h)
|
|
|
|
}
|
|
|
|
if m > 0 {
|
|
|
|
res = fmt.Sprintf("%s%dM", res, m)
|
|
|
|
}
|
|
|
|
if s > 0 {
|
|
|
|
res = fmt.Sprintf("%s%dS", res, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|