github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/communicator/winrm/provisioner.go (about) 1 package winrm 2 3 import ( 4 "fmt" 5 "log" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/terraform/communicator/shared" 11 "github.com/hashicorp/terraform/terraform" 12 "github.com/mitchellh/mapstructure" 13 ) 14 15 const ( 16 // DefaultUser is used if there is no user given 17 DefaultUser = "Administrator" 18 19 // DefaultPort is used if there is no port given 20 DefaultPort = 5985 21 22 // DefaultHTTPSPort is used if there is no port given and HTTPS is true 23 DefaultHTTPSPort = 5986 24 25 // DefaultScriptPath is used as the path to copy the file to 26 // for remote execution if not provided otherwise. 27 DefaultScriptPath = "C:/Temp/terraform_%RAND%.cmd" 28 29 // DefaultTimeout is used if there is no timeout given 30 DefaultTimeout = 5 * time.Minute 31 ) 32 33 // connectionInfo is decoded from the ConnInfo of the resource. These are the 34 // only keys we look at. If a KeyFile is given, that is used instead 35 // of a password. 36 type connectionInfo struct { 37 User string 38 Password string 39 Host string 40 Port int 41 HTTPS bool 42 Insecure bool 43 NTLM bool `mapstructure:"use_ntlm"` 44 CACert string `mapstructure:"cacert"` 45 Timeout string 46 ScriptPath string `mapstructure:"script_path"` 47 TimeoutVal time.Duration `mapstructure:"-"` 48 } 49 50 // parseConnectionInfo is used to convert the ConnInfo of the InstanceState into 51 // a ConnectionInfo struct 52 func parseConnectionInfo(s *terraform.InstanceState) (*connectionInfo, error) { 53 connInfo := &connectionInfo{} 54 decConf := &mapstructure.DecoderConfig{ 55 WeaklyTypedInput: true, 56 Result: connInfo, 57 } 58 dec, err := mapstructure.NewDecoder(decConf) 59 if err != nil { 60 return nil, err 61 } 62 if err := dec.Decode(s.Ephemeral.ConnInfo); err != nil { 63 return nil, err 64 } 65 66 // Check on script paths which point to the default Windows TEMP folder because files 67 // which are put in there very early in the boot process could get cleaned/deleted 68 // before you had the change to execute them. 69 // 70 // TODO (SvH) Needs some more debugging to fully understand the exact sequence of events 71 // causing this... 72 if strings.HasPrefix(filepath.ToSlash(connInfo.ScriptPath), "C:/Windows/Temp") { 73 return nil, fmt.Errorf( 74 `Using the C:\Windows\Temp folder is not supported. Please use a different 'script_path'.`) 75 } 76 77 if connInfo.User == "" { 78 connInfo.User = DefaultUser 79 } 80 81 // Format the host if needed. 82 // Needed for IPv6 support. 83 connInfo.Host = shared.IpFormat(connInfo.Host) 84 85 if connInfo.Port == 0 { 86 if connInfo.HTTPS { 87 connInfo.Port = DefaultHTTPSPort 88 } else { 89 connInfo.Port = DefaultPort 90 } 91 } 92 if connInfo.ScriptPath == "" { 93 connInfo.ScriptPath = DefaultScriptPath 94 } 95 if connInfo.Timeout != "" { 96 connInfo.TimeoutVal = safeDuration(connInfo.Timeout, DefaultTimeout) 97 } else { 98 connInfo.TimeoutVal = DefaultTimeout 99 } 100 101 return connInfo, nil 102 } 103 104 // safeDuration returns either the parsed duration or a default value 105 func safeDuration(dur string, defaultDur time.Duration) time.Duration { 106 d, err := time.ParseDuration(dur) 107 if err != nil { 108 log.Printf("Invalid duration '%s', using default of %s", dur, defaultDur) 109 return defaultDur 110 } 111 return d 112 } 113 114 func formatDuration(duration time.Duration) string { 115 h := int(duration.Hours()) 116 m := int(duration.Minutes()) - h*60 117 s := int(duration.Seconds()) - (h*3600 + m*60) 118 119 res := "PT" 120 if h > 0 { 121 res = fmt.Sprintf("%s%dH", res, h) 122 } 123 if m > 0 { 124 res = fmt.Sprintf("%s%dM", res, m) 125 } 126 if s > 0 { 127 res = fmt.Sprintf("%s%dS", res, s) 128 } 129 130 return res 131 }