github.com/kevinklinger/open_terraform@v1.3.6/noninternal/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/kevinklinger/open_terraform/noninternal/communicator/shared" 11 "github.com/zclconf/go-cty/cty" 12 "github.com/zclconf/go-cty/cty/gocty" 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 uint16 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 // decodeConnInfo decodes the given cty.Value using the same behavior as the 51 // lgeacy mapstructure decoder in order to preserve as much of the existing 52 // logic as possible for compatibility. 53 func decodeConnInfo(v cty.Value) (*connectionInfo, error) { 54 connInfo := &connectionInfo{} 55 if v.IsNull() { 56 return connInfo, nil 57 } 58 59 for k, v := range v.AsValueMap() { 60 if v.IsNull() { 61 continue 62 } 63 64 switch k { 65 case "user": 66 connInfo.User = v.AsString() 67 case "password": 68 connInfo.Password = v.AsString() 69 case "host": 70 connInfo.Host = v.AsString() 71 case "port": 72 if err := gocty.FromCtyValue(v, &connInfo.Port); err != nil { 73 return nil, err 74 } 75 case "https": 76 connInfo.HTTPS = v.True() 77 case "insecure": 78 connInfo.Insecure = v.True() 79 case "use_ntlm": 80 connInfo.NTLM = v.True() 81 case "cacert": 82 connInfo.CACert = v.AsString() 83 case "script_path": 84 connInfo.ScriptPath = v.AsString() 85 case "timeout": 86 connInfo.Timeout = v.AsString() 87 } 88 } 89 return connInfo, nil 90 } 91 92 // parseConnectionInfo is used to convert the ConnInfo of the InstanceState into 93 // a ConnectionInfo struct 94 func parseConnectionInfo(v cty.Value) (*connectionInfo, error) { 95 v, err := shared.ConnectionBlockSupersetSchema.CoerceValue(v) 96 if err != nil { 97 return nil, err 98 } 99 100 connInfo, err := decodeConnInfo(v) 101 if err != nil { 102 return nil, err 103 } 104 // Check on script paths which point to the default Windows TEMP folder because files 105 // which are put in there very early in the boot process could get cleaned/deleted 106 // before you had the change to execute them. 107 // 108 // TODO (SvH) Needs some more debugging to fully understand the exact sequence of events 109 // causing this... 110 if strings.HasPrefix(filepath.ToSlash(connInfo.ScriptPath), "C:/Windows/Temp") { 111 return nil, fmt.Errorf( 112 `Using the C:\Windows\Temp folder is not supported. Please use a different 'script_path'.`) 113 } 114 115 if connInfo.User == "" { 116 connInfo.User = DefaultUser 117 } 118 119 // Format the host if needed. 120 // Needed for IPv6 support. 121 connInfo.Host = shared.IpFormat(connInfo.Host) 122 123 if connInfo.Port == 0 { 124 if connInfo.HTTPS { 125 connInfo.Port = DefaultHTTPSPort 126 } else { 127 connInfo.Port = DefaultPort 128 } 129 } 130 if connInfo.ScriptPath == "" { 131 connInfo.ScriptPath = DefaultScriptPath 132 } 133 if connInfo.Timeout != "" { 134 connInfo.TimeoutVal = safeDuration(connInfo.Timeout, DefaultTimeout) 135 } else { 136 connInfo.TimeoutVal = DefaultTimeout 137 } 138 139 return connInfo, nil 140 } 141 142 // safeDuration returns either the parsed duration or a default value 143 func safeDuration(dur string, defaultDur time.Duration) time.Duration { 144 d, err := time.ParseDuration(dur) 145 if err != nil { 146 log.Printf("Invalid duration '%s', using default of %s", dur, defaultDur) 147 return defaultDur 148 } 149 return d 150 } 151 152 func formatDuration(duration time.Duration) string { 153 h := int(duration.Hours()) 154 m := int(duration.Minutes()) - h*60 155 s := int(duration.Seconds()) - (h*3600 + m*60) 156 157 res := "PT" 158 if h > 0 { 159 res = fmt.Sprintf("%s%dH", res, h) 160 } 161 if m > 0 { 162 res = fmt.Sprintf("%s%dM", res, m) 163 } 164 if s > 0 { 165 res = fmt.Sprintf("%s%dS", res, s) 166 } 167 168 return res 169 }