|
| 1 | +# Oracle Cloud Infrastructure Terraform Provider Example for Windows VM |
| 2 | + |
| 3 | +This example contains Terraform configuration to provision a virtual machine in Oracle Cloud Infrastructure (OCI) with Microsoft Windows. |
| 4 | + |
| 5 | +## What this example covers |
| 6 | + |
| 7 | +- Deploying networking resources that creates a VCN, Subnet, Route Table, Internet Gateway and a Security List to allow RDP & WinRM traffic for the VM |
| 8 | + - [networking.tf](networking.tf) |
| 9 | +- Deploying a Windows VM instance with one of [published images on OCI](https://docs.cloud.oracle.com/iaas/images/) |
| 10 | + - [windows.tf](windows.tf) |
| 11 | +- Using the Windows version for [Cloud-Init](https://cloud-init.io/) - [Cloudbase-Init](https://cloudbase.it/cloudbase-init/) available on the VM to setup and configure Windows |
| 12 | + - [cloudinit.ps1](userdata/cloudinit.ps1) - #ps1_sysnative |
| 13 | + - Change the initial Windows VM Password |
| 14 | + - Configure WinRM for HTTPS connections |
| 15 | + - [cloudinit.yml](userdata/cloudinit.yml) - #cloud-config |
| 16 | + - Write custom files |
| 17 | +- Using Terraform remote-exec execute custom scripts through WinRM |
| 18 | + - [winrm.tf](winrm.tf) |
| 19 | + - [setup.ps1](userdata/setup.ps1) |
| 20 | + - Mount block volumes (iscsi) attached to the VM |
| 21 | + |
| 22 | +## CloudInit |
| 23 | + |
| 24 | +- The latest [Windows Images](https://docs.cloud.oracle.com/iaas/images/windows-server-2012-r2-vm/) released in and after July 2018 for OCI come with Windows version of [Cloud-Init](https://cloud-init.io/), [Cloudbase-Init](https://cloudbase.it/cloudbase-init/) and WinRM enabled by default, refer to the release notes of the [images](https://docs.cloud.oracle.com/iaas/images/) to ensure the version you choose has these enabled for this example to run |
| 25 | +- There are multiple ways in which CloudBase-Init can be used by providing a Base64 encoded metadata in [LaunchInstanceDetails](https://docs.cloud.oracle.com/iaas/api/#/en/iaas/20160918/datatypes/LaunchInstanceDetails), this example uses the #cloud-config and #ps1_sysnative variations |
| 26 | +- All the plugins in #cloud-config format may not be supported yet, currently script and runcmd are not supported, but some other plugins like write_files are |
| 27 | + - Alternatively, to run custom Windows shell commands or Powershell commands, one can alternately use #ps1_sysnative to have them execute as part of initial setup |
| 28 | + |
| 29 | +## WinRM |
| 30 | + |
| 31 | +- While WinRM is enabled on the images, if you plan to use your own images, you need to configure it using following commands. |
| 32 | + |
| 33 | +```powershell |
| 34 | + winrm quickconfig |
| 35 | + Enable-PSRemoting |
| 36 | +
|
| 37 | + winrm set winrm/config/client/auth '@{Basic="true"}' |
| 38 | + winrm set winrm/config/service/auth '@{Basic="true"}' |
| 39 | + winrm set winrm/config/service '@{AllowUnencrypted="true"}' |
| 40 | + winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="300"}' |
| 41 | + winrm set winrm/config '@{MaxTimeoutms="1800000"}' |
| 42 | +
|
| 43 | + netsh advfirewall firewall add rule name="WinRM HTTP" protocol=TCP dir=in profile=any localport=5985 remoteip=any localip=any action=allow |
| 44 | + netsh advfirewall firewall add rule name="WinRM HTTPS" protocol=TCP dir=in profile=any localport=5986 remoteip=any localip=any action=allow |
| 45 | +
|
| 46 | + net stop winrm |
| 47 | + sc.exe config winrm start=auto |
| 48 | + net start winrm |
| 49 | +``` |
| 50 | + |
| 51 | +- Strongly consider the security aspects of allowing unencrypted connections (HTTP). This example shows how to create a self-signed certificate using Cloudbase-Init to configure WinRM for HTTPS communication |
| 52 | +- Based on what ports you have configured for RDP and WinRM you want to setup the Security List for your VCN to allow those ports, this example covers these ports: |
| 53 | + - 3389 - RDP |
| 54 | + - 5985 - WinRM HTTP |
| 55 | + - 5986 - WinRM HTTPS |
| 56 | + |
| 57 | +## Terraform |
| 58 | + |
| 59 | +- Terraform uses the Go based winrm(<https://github.com/masterzen/winrm>) library to make remote-exec connections, this library supports BasicAuth by default and can be extended to support additional authentication models |
| 60 | +- The remote-exec in Terraform uses SSH by default, but the type can be changed to `winrm` to execute remote commands on Windows |
| 61 | +- Terraform provider supports both `template_file` and `template_cloudinit_config` data sources that can be used to set metadata for LaunchInstanceDetails |
| 62 | + |
| 63 | +## Tips and Troubleshooting |
| 64 | + |
| 65 | +- CloudBase-Init is installed at `C:\Program Files\Cloudbase Solutions\Cloudbase-Init` and you can find its logs in the `log` directory under it. Refer [cloudbase-init tutorial](https://cloudbase-init.readthedocs.io/en/latest/tutorial.html) for more information. |
| 66 | +- Use #ps1_sysnative for more advanced VM configuration through Cloudbase-init |
| 67 | +- The Cloudbase-Init setup may not have completed when the VM is reported to be ready, you can either introduce a wait or have the VM write to a remote location that you can poll before launching remote-exec via Terraform, this examples waits 60 seconds |
| 68 | +- The VM instance Cloud-Init metadata that is passed to LaunchInstanceDetails and then read over in VM is just Base64 encoded, you may want to transfer the new password in a more secure way or change it through another remote-exec that can run post Cloudbase-Init. Further, the example also has the passwords stored in the local state file. |
| 69 | + - Refer Terraform recommendations for [Sensitive Data](https://www.terraform.io/docs/state/sensitive-data.html) |
| 70 | +- The example covers running various Powershell commands, but for a more reliable solution, you may want to add enough retries and error reporting for setup resiliency |
| 71 | +- While setting up HTTP over BasicAuth is easy, it is not a recommended way to connecting to these VMs, consider using HTTPS by configuring WinRM HTTPS listener using your own certificate |
| 72 | +- If you are facing certificate based errors for WinRM HTTPS connection it is probably due to using the self-signed certificate using New-SelfSignedCertificate that WinRM does not find compatible in newer operating systems |
| 73 | + - Ideally you should use CA based certificate for configuring WinRM |
| 74 | + - Alternatively, you can use this Ansible published script [ConfigureRemotingForAnsible.ps1](https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1) to generate a legacy self-signed certificate and configure WinRM to use same. |
| 75 | + - This entire script can be passed as an additional part in `template_cloudinit_config` to configure WinRM for HTTPS with a self-signed certificate. If you do so, remove the certificate based section from `cloudinit.ps1` in this example |
| 76 | + - To get detailed error and test WinRM connectivity from within the VM, you can use the following commands |
| 77 | + ```powershell |
| 78 | + $cred=Get-Credential |
| 79 | + test-wsman -Authentication Basic -UseSSL -Credential $cred |
| 80 | + ``` |
| 81 | +- To test if WinRM is correctly configured on the VM and is listening, you can use any of the following methods to troubleshoot |
| 82 | +
|
| 83 | +```powershell |
| 84 | + # To check if winrm is listening |
| 85 | + curl --header "Content-Type: application/soap+xml;charset=UTF-8" --header "WSMANIDENTIFY: unauthenticated" --insecure https://<ip-address>:5986/wsman --data '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd"><s:Header/><s:Body><wsmid:Identify/></s:Body></s:Envelope>' |
| 86 | +
|
| 87 | + # To check winrm with authentication: |
| 88 | + curl --header "Content-Type: application/soap+xml;charset=UTF-8" --insecure https://<ip-address>:5986/wsman --basic -u opc:password --data '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd"><s:Header/><s:Body><wsmid:Identify/></s:Body></s:Envelope>' |
| 89 | +
|
| 90 | + # Using winrm-cli from https://github.com/masterzen/winrm-cli that uses same underlying library that Terraform uses: https://github.com/masterzen/winrm |
| 91 | + ./winrm -hostname <ip-address> -username "opc" -password "password" -https -insecure "ipconfig /all" |
| 92 | +``` |
| 93 | + |
| 94 | +- From the VM instance you can run the following commands to get the metadata |
| 95 | + |
| 96 | +```powershell |
| 97 | + curl http://169.254.169.254/opc/v1/instance/ |
| 98 | + curl http://169.254.169.254/opc/v1/instance/metadata/ |
| 99 | + curl http://169.254.169.254/opc/v1/instance/metadata/<any-key-name> |
| 100 | +
|
| 101 | + # To get user_data |
| 102 | + curl http://169.254.169.254/opc/v1/instance/metadata/user_data |
| 103 | +``` |
| 104 | + |
| 105 | +- If you are facing issues with connecting or using WinRM from Terraform through remote-exec, an alternative approach can be to use local-exec with another library like [pywinrm](https://github.com/diyan/pywinrm) |
0 commit comments