github.com/hashiCorp/terraform@v0.11.12-beta1/communicator/winrm/communicator.go (about) 1 package winrm 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "math/rand" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/hashicorp/terraform/communicator/remote" 13 "github.com/hashicorp/terraform/terraform" 14 "github.com/masterzen/winrm" 15 "github.com/packer-community/winrmcp/winrmcp" 16 ) 17 18 // Communicator represents the WinRM communicator 19 type Communicator struct { 20 connInfo *connectionInfo 21 client *winrm.Client 22 endpoint *winrm.Endpoint 23 rand *rand.Rand 24 } 25 26 // New creates a new communicator implementation over WinRM. 27 func New(s *terraform.InstanceState) (*Communicator, error) { 28 connInfo, err := parseConnectionInfo(s) 29 if err != nil { 30 return nil, err 31 } 32 33 endpoint := &winrm.Endpoint{ 34 Host: connInfo.Host, 35 Port: connInfo.Port, 36 HTTPS: connInfo.HTTPS, 37 Insecure: connInfo.Insecure, 38 } 39 if len(connInfo.CACert) > 0 { 40 endpoint.CACert = []byte(connInfo.CACert) 41 } 42 43 comm := &Communicator{ 44 connInfo: connInfo, 45 endpoint: endpoint, 46 // Seed our own rand source so that script paths are not deterministic 47 rand: rand.New(rand.NewSource(time.Now().UnixNano())), 48 } 49 50 return comm, nil 51 } 52 53 // Connect implementation of communicator.Communicator interface 54 func (c *Communicator) Connect(o terraform.UIOutput) error { 55 if c.client != nil { 56 return nil 57 } 58 59 params := winrm.DefaultParameters 60 params.Timeout = formatDuration(c.Timeout()) 61 if c.connInfo.NTLM == true { 62 params.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} } 63 } 64 65 client, err := winrm.NewClientWithParameters( 66 c.endpoint, c.connInfo.User, c.connInfo.Password, params) 67 if err != nil { 68 return err 69 } 70 71 if o != nil { 72 o.Output(fmt.Sprintf( 73 "Connecting to remote host via WinRM...\n"+ 74 " Host: %s\n"+ 75 " Port: %d\n"+ 76 " User: %s\n"+ 77 " Password: %t\n"+ 78 " HTTPS: %t\n"+ 79 " Insecure: %t\n"+ 80 " NTLM: %t\n"+ 81 " CACert: %t", 82 c.connInfo.Host, 83 c.connInfo.Port, 84 c.connInfo.User, 85 c.connInfo.Password != "", 86 c.connInfo.HTTPS, 87 c.connInfo.Insecure, 88 c.connInfo.NTLM, 89 c.connInfo.CACert != "", 90 )) 91 } 92 93 log.Printf("[DEBUG] connecting to remote shell using WinRM") 94 shell, err := client.CreateShell() 95 if err != nil { 96 log.Printf("[ERROR] error creating shell: %s", err) 97 return err 98 } 99 100 err = shell.Close() 101 if err != nil { 102 log.Printf("[ERROR] error closing shell: %s", err) 103 return err 104 } 105 106 if o != nil { 107 o.Output("Connected!") 108 } 109 110 c.client = client 111 112 return nil 113 } 114 115 // Disconnect implementation of communicator.Communicator interface 116 func (c *Communicator) Disconnect() error { 117 c.client = nil 118 return nil 119 } 120 121 // Timeout implementation of communicator.Communicator interface 122 func (c *Communicator) Timeout() time.Duration { 123 return c.connInfo.TimeoutVal 124 } 125 126 // ScriptPath implementation of communicator.Communicator interface 127 func (c *Communicator) ScriptPath() string { 128 return strings.Replace( 129 c.connInfo.ScriptPath, "%RAND%", 130 strconv.FormatInt(int64(c.rand.Int31()), 10), -1) 131 } 132 133 // Start implementation of communicator.Communicator interface 134 func (c *Communicator) Start(rc *remote.Cmd) error { 135 rc.Init() 136 log.Printf("[DEBUG] starting remote command: %s", rc.Command) 137 138 // TODO: make sure communicators always connect first, so we can get output 139 // from the connection. 140 if c.client == nil { 141 log.Println("[WARN] winrm client not connected, attempting to connect") 142 if err := c.Connect(nil); err != nil { 143 return err 144 } 145 } 146 147 status, err := c.client.Run(rc.Command, rc.Stdout, rc.Stderr) 148 rc.SetExitStatus(status, err) 149 150 return nil 151 } 152 153 // Upload implementation of communicator.Communicator interface 154 func (c *Communicator) Upload(path string, input io.Reader) error { 155 wcp, err := c.newCopyClient() 156 if err != nil { 157 return err 158 } 159 log.Printf("[DEBUG] Uploading file to '%s'", path) 160 return wcp.Write(path, input) 161 } 162 163 // UploadScript implementation of communicator.Communicator interface 164 func (c *Communicator) UploadScript(path string, input io.Reader) error { 165 return c.Upload(path, input) 166 } 167 168 // UploadDir implementation of communicator.Communicator interface 169 func (c *Communicator) UploadDir(dst string, src string) error { 170 log.Printf("[DEBUG] Uploading dir '%s' to '%s'", src, dst) 171 wcp, err := c.newCopyClient() 172 if err != nil { 173 return err 174 } 175 return wcp.Copy(src, dst) 176 } 177 178 func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) { 179 addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port) 180 181 config := winrmcp.Config{ 182 Auth: winrmcp.Auth{ 183 User: c.connInfo.User, 184 Password: c.connInfo.Password, 185 }, 186 Https: c.connInfo.HTTPS, 187 Insecure: c.connInfo.Insecure, 188 OperationTimeout: c.Timeout(), 189 MaxOperationsPerShell: 15, // lowest common denominator 190 } 191 192 if c.connInfo.NTLM == true { 193 config.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} } 194 } 195 196 if c.connInfo.CACert != "" { 197 config.CACertBytes = []byte(c.connInfo.CACert) 198 } 199 200 return winrmcp.New(addr, &config) 201 }