Skip to content

Commit

Permalink
feat: enable bring your own license for custom linux images (#351)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludwig-mueller authored Jan 15, 2025
1 parent 0e411d0 commit adcf548
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 10 deletions.
14 changes: 11 additions & 3 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": "go.sum|^.secrets.baseline$",
"lines": null
},
"generated_at": "2025-01-03T11:24:59Z",
"generated_at": "2025-01-09T14:24:18Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
Expand Down Expand Up @@ -86,19 +86,27 @@
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "9ceaacf8f9b3c35bd235b307d91a5bf7cff2c669",
"is_secret": false,
"is_verified": false,
"line_number": 68,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "2254481e1661d8f017a712b0d1ad9a14fd9460a3",
"is_secret": false,
"is_verified": false,
"line_number": 119,
"line_number": 121,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "74740d3929318426a870c3d1dc98bc2b35d648fd",
"is_secret": false,
"is_verified": false,
"line_number": 119,
"line_number": 121,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ module "pi_instance" {
pi_storage_config = var.pi_storage_config #(optional, default check vars)
pi_instance_init_linux = var.pi_instance_init_linux #(optional, default check vars)
pi_network_services_config = var.pi_network_services_config #(optional, default check vars)
pi_user_tags = var.pi_user_tags #(optional), default null
pi_user_tags = var.pi_user_tags #(optional, default null)
ansible_vault_password = var.ansible_vault_password #(optional, default null)
}
```

Expand Down Expand Up @@ -111,12 +112,13 @@ No resources.

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_ansible_vault_password"></a> [ansible\_vault\_password](#input\_ansible\_vault\_password) | Vault password to encrypt OS registration parameters. Only required with customer provided linux subscription (specified in pi\_instance\_init\_linux.custom\_os\_registration). For optimal security, set the vault password to 8-16 characters, including a mix of uppercase, lowercase, numbers, and special characters. Avoid non-printable characters. | `string` | `null` | no |
| <a name="input_pi_boot_image_storage_pool"></a> [pi\_boot\_image\_storage\_pool](#input\_pi\_boot\_image\_storage\_pool) | Storage Pool for server deployment; Only valid when you deploy one of the IBM supplied stock images. Storage pool for a custom image (an imported image or an image that is created from a VM capture) defaults to the storage pool the image was created in. | `string` | `null` | no |
| <a name="input_pi_boot_image_storage_tier"></a> [pi\_boot\_image\_storage\_tier](#input\_pi\_boot\_image\_storage\_tier) | Storage type for server deployment. If storage type is not provided the storage type will default to tier3. Possible values tier0, tier1 and tier3 | `string` | `null` | no |
| <a name="input_pi_cpu_proc_type"></a> [pi\_cpu\_proc\_type](#input\_pi\_cpu\_proc\_type) | The type of processor mode in which the VM will run with shared, capped or dedicated. Required when not creating SAP instances. Conflicts with 'pi\_sap\_profile\_id'. | `string` | `null` | no |
| <a name="input_pi_existing_volume_ids"></a> [pi\_existing\_volume\_ids](#input\_pi\_existing\_volume\_ids) | List of existing volume ids that must be attached to the instance. | `list(string)` | `null` | no |
| <a name="input_pi_image_id"></a> [pi\_image\_id](#input\_pi\_image\_id) | Image ID used for PowerVS instance. Run 'ibmcloud pi images' to list available images. | `string` | n/a | yes |
| <a name="input_pi_instance_init_linux"></a> [pi\_instance\_init\_linux](#input\_pi\_instance\_init\_linux) | Configures a PowerVS linux instance to have internet access by setting proxy on it, updates os and create filesystems using ansible collection [ibm.power\_linux\_sap collection](https://galaxy.ansible.com/ui/repo/published/ibm/power_linux_sap/) where 'bastion\_host\_ip' is public IP of bastion/jump host to access the 'ansible\_host\_or\_ip' private IP of ansible node. This ansible host must have access to the power virtual server instance and ansible host OS must be RHEL distribution. | <pre>object(<br/> {<br/> enable = bool<br/> bastion_host_ip = string<br/> ansible_host_or_ip = string<br/> ssh_private_key = string<br/> }<br/> )</pre> | <pre>{<br/> "ansible_host_or_ip": "",<br/> "bastion_host_ip": "",<br/> "enable": false,<br/> "ssh_private_key": ""<br/>}</pre> | no |
| <a name="input_pi_instance_init_linux"></a> [pi\_instance\_init\_linux](#input\_pi\_instance\_init\_linux) | Configures a PowerVS linux instance to have internet access by setting proxy on it, updates os and create filesystems using ansible collection [ibm.power\_linux\_sap collection](https://galaxy.ansible.com/ui/repo/published/ibm/power_linux_sap/) where 'bastion\_host\_ip' is public IP of bastion/jump host to access the 'ansible\_host\_or\_ip' private IP of ansible node. Additionally, specify whether IBM provided or customer provided linux subscription should be used. For IBM provided subscription leave custom\_os\_registration empty. For customer provided subscription set a username and a password inside custom\_os\_registration. Customer provided linux subscription requires the use of either an IBM provided image ending in BYOL or a custom image. The ansible host must have access to the power virtual server instance and ansible host OS must be RHEL distribution. | <pre>object(<br/> {<br/> enable = bool<br/> bastion_host_ip = string<br/> ansible_host_or_ip = string<br/> ssh_private_key = string<br/> custom_os_registration = optional(object({<br/> username = string<br/> password = string<br/> }))<br/> }<br/> )</pre> | <pre>{<br/> "ansible_host_or_ip": "",<br/> "bastion_host_ip": "",<br/> "enable": false,<br/> "ssh_private_key": ""<br/>}</pre> | no |
| <a name="input_pi_instance_name"></a> [pi\_instance\_name](#input\_pi\_instance\_name) | Name of instance which will be created. | `string` | n/a | yes |
| <a name="input_pi_memory_size"></a> [pi\_memory\_size](#input\_pi\_memory\_size) | The amount of memory that you want to assign to your instance in GB. Required when not creating SAP instances. Conflicts with 'pi\_sap\_profile\_id'. | `string` | `null` | no |
| <a name="input_pi_network_services_config"></a> [pi\_network\_services\_config](#input\_pi\_network\_services\_config) | Configures network services proxy, NTP, NFS and DNS on PowerVS instance. Requires 'pi\_instance\_init\_linux' to be specified to configure these services. The 'opts' attribute can take in comma separated values. | <pre>object(<br/> {<br/> squid = object({ enable = bool, squid_server_ip_port = string, no_proxy_hosts = string })<br/> nfs = object({ enable = bool, nfs_server_path = string, nfs_client_path = string, opts = string, fstype = string })<br/> dns = object({ enable = bool, dns_server_ip = string })<br/> ntp = object({ enable = bool, ntp_server_ip = string })<br/> }<br/> )</pre> | <pre>{<br/> "dns": {<br/> "dns_server_ip": "",<br/> "enable": false<br/> },<br/> "nfs": {<br/> "enable": false,<br/> "fstype": "",<br/> "nfs_client_path": "",<br/> "nfs_server_path": "",<br/> "opts": ""<br/> },<br/> "ntp": {<br/> "enable": false,<br/> "ntp_server_ip": ""<br/> },<br/> "squid": {<br/> "enable": false,<br/> "no_proxy_hosts": "",<br/> "squid_server_ip_port": ""<br/> }<br/>}</pre> | no |
Expand Down
3 changes: 2 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ module "pi_instance_init_linux" {

src_playbook_template_name = "linux-init/playbook-linux-init.yml.tftpl"
dst_playbook_file_name = "${var.pi_instance_name}-playbook-linux-init.yml"
playbook_template_vars = { "pi_storage_config" : jsonencode(module.pi_instance.pi_storage_configuration), "client_config" : jsonencode(var.pi_network_services_config) }
playbook_template_vars = { "pi_storage_config" : jsonencode(module.pi_instance.pi_storage_configuration), "client_config" : jsonencode(var.pi_network_services_config), "pi_os_registration" : jsonencode(var.pi_instance_init_linux.custom_os_registration) }

src_inventory_template_name = "pi-instance-inventory.tftpl"
dst_inventory_file_name = "${var.pi_instance_name}-instance-inventory"
inventory_template_vars = { "pi_instance_management_ip" : module.pi_instance.pi_instance_primary_ip }

ansible_vault_password = var.ansible_vault_password
}
90 changes: 89 additions & 1 deletion modules/ansible/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ resource "terraform_data" "trigger_ansible_vars" {

resource "terraform_data" "execute_playbooks" {
depends_on = [terraform_data.setup_ansible_host]
count = var.ansible_vault_password != null ? 0 : 1

connection {
type = "ssh"
Expand Down Expand Up @@ -103,7 +104,8 @@ resource "terraform_data" "execute_playbooks" {
"ansible_playbook_file" : local.dst_playbook_file_path,
"ansible_log_path" : local.dst_files_dir,
"ansible_inventory" : local.dst_inventory_file_path
"ansible_private_key_file" : local.private_key_file
"ansible_private_key_file" : local.private_key_file,
"ansible_vault_password" : var.ansible_vault_password
})
destination = local.dst_script_file_path
}
Expand Down Expand Up @@ -133,3 +135,89 @@ resource "terraform_data" "execute_playbooks" {
]
}
}

resource "terraform_data" "execute_playbooks_with_vault" {
depends_on = [terraform_data.setup_ansible_host]
count = var.ansible_vault_password != null ? 1 : 0

connection {
type = "ssh"
user = "root"
bastion_host = var.bastion_host_ip
host = var.ansible_host_or_ip
private_key = var.ssh_private_key
agent = false
timeout = "5m"
}

triggers_replace = terraform_data.trigger_ansible_vars

# Create terraform scripts directory
provisioner "remote-exec" {
inline = ["mkdir -p ${local.dst_files_dir}", "chmod 777 ${local.dst_files_dir}", ]
}

# Copy and create ansible playbook template file on ansible host
provisioner "file" {
content = templatefile(local.src_playbook_tftpl_path, var.playbook_template_vars)
destination = local.dst_playbook_file_path
}

######### Encrypting the ansible playbook file when ansible_vault_password is set (only set if os_registration parameters are included) #########
provisioner "remote-exec" {
inline = [
"echo ${var.ansible_vault_password} > password_file",
"ansible-vault encrypt ${local.dst_playbook_file_path} --vault-password-file password_file"
]
}

# Copy and create ansible inventory template file on ansible host
provisioner "file" {
content = templatefile(local.src_inventory_tftpl_path, var.inventory_template_vars)
destination = local.dst_inventory_file_path
}

# Copy and create ansible shell template file which will trigger the playbook on ansible host
provisioner "file" {
content = templatefile(local.src_script_tftpl_path,
{
"ansible_playbook_file" : local.dst_playbook_file_path,
"ansible_log_path" : local.dst_files_dir,
"ansible_inventory" : local.dst_inventory_file_path
"ansible_private_key_file" : local.private_key_file,
"ansible_vault_password" : var.ansible_vault_password
})
destination = local.dst_script_file_path
}

# Write ssh user's ssh private key
provisioner "remote-exec" {
inline = [
"mkdir -p /root/.ssh/",
"chmod 700 /root/.ssh",
"echo '${var.ssh_private_key}' > ${local.private_key_file}",
"chmod 600 ${local.private_key_file}",
]
}

# Execute bash shell script to run ansible playbooks
provisioner "remote-exec" {
inline = [
"chmod +x ${local.dst_script_file_path}",
local.dst_script_file_path,
]
}

# Again delete private ssh key and files with sensitive information
provisioner "remote-exec" {
inline = [
"rm -rf ${local.private_key_file}",
"rm -rf password_file"
]
}
}

moved {
from = terraform_data.execute_playbooks
to = terraform_data.execute_playbooks[0]
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ export ANSIBLE_LOG_PATH=$${ansible_log_path}/$${ansible_playbook_name}.$(date "+
export ANSIBLE_PRIVATE_KEY_FILE=$${ansible_private_key_file}

#Execute ansible playbook
unbuffer ansible-playbook -i $${ansible_inventory} $${ansible_playbook}
unbuffer ansible-playbook -i $${ansible_inventory} $${ansible_playbook} %{ if ansible_vault_password != null}--vault-password-file password_file %{ endif }
if [ $? -ne 0 ]; then
rm -rf $${ansible_private_key_file}
%{ if ansible_vault_password != null}rm -rf password_file %{ endif }
exit 1
fi
echo \"Playbook command successful\"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
- name: Ansible play to perform OS registration
hosts: all
vars:
%{~ if pi_os_registration == "null" ~}
fls: true
%{~ else ~}
byol:
username: "${jsondecode(pi_os_registration).username}"
password: "${jsondecode(pi_os_registration).password}"
server_proxy_hostname: "${split(":", jsondecode(client_config).squid.squid_server_ip_port)[0]}"
server_proxy_port: "${split(":", jsondecode(client_config).squid.squid_server_ip_port)[1]}"
%{~ endif ~}

roles:
- { role: ibm.power_linux_sap.powervs_os_registration}
- { role: ibm.power_linux_sap.powervs_os_registration, when: byol is defined or fls is defined}

- name: Ansible play to configure Network services proxy, DNS, NTP and NFS
hosts: all
Expand Down
7 changes: 7 additions & 0 deletions modules/ansible/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,10 @@ variable "inventory_template_vars" {
description = "Map values for the inventory template."
type = map(any)
}

variable "ansible_vault_password" {
description = "Vault password to encrypt OS registration parameters. For optimal security, set the vault password to 8-16 characters, including a mix of uppercase, lowercase, numbers, and special characters. Avoid non-printable characters. Required only if you bring your own linux license."
type = string
sensitive = true
default = null
}
17 changes: 16 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,18 @@ variable "pi_user_tags" {
#####################################################

variable "pi_instance_init_linux" {
description = "Configures a PowerVS linux instance to have internet access by setting proxy on it, updates os and create filesystems using ansible collection [ibm.power_linux_sap collection](https://galaxy.ansible.com/ui/repo/published/ibm/power_linux_sap/) where 'bastion_host_ip' is public IP of bastion/jump host to access the 'ansible_host_or_ip' private IP of ansible node. This ansible host must have access to the power virtual server instance and ansible host OS must be RHEL distribution."
description = "Configures a PowerVS linux instance to have internet access by setting proxy on it, updates os and create filesystems using ansible collection [ibm.power_linux_sap collection](https://galaxy.ansible.com/ui/repo/published/ibm/power_linux_sap/) where 'bastion_host_ip' is public IP of bastion/jump host to access the 'ansible_host_or_ip' private IP of ansible node. Additionally, specify whether IBM provided or customer provided linux subscription should be used. For IBM provided subscription leave custom_os_registration empty. For customer provided subscription set a username and a password inside custom_os_registration. Customer provided linux subscription requires the use of either an IBM provided image ending in BYOL or a custom image. The ansible host must have access to the power virtual server instance and ansible host OS must be RHEL distribution."
sensitive = true
type = object(
{
enable = bool
bastion_host_ip = string
ansible_host_or_ip = string
ssh_private_key = string
custom_os_registration = optional(object({
username = string
password = string
}))
}
)

Expand All @@ -167,6 +171,17 @@ EOF
}
}

variable "ansible_vault_password" {
description = "Vault password to encrypt OS registration parameters. Only required with customer provided linux subscription (specified in pi_instance_init_linux.custom_os_registration). For optimal security, set the vault password to 8-16 characters, including a mix of uppercase, lowercase, numbers, and special characters. Avoid non-printable characters."
type = string
sensitive = true
default = null
validation {
condition = var.pi_instance_init_linux.custom_os_registration == null ? true : var.ansible_vault_password != null
error_message = "Specifying custom_os_registration requires an ansible_vault_password so your credentials are stored securely."
}
}

variable "pi_network_services_config" {
description = "Configures network services proxy, NTP, NFS and DNS on PowerVS instance. Requires 'pi_instance_init_linux' to be specified to configure these services. The 'opts' attribute can take in comma separated values."
type = object(
Expand Down

0 comments on commit adcf548

Please sign in to comment.