Skill Level: Intermediate

This tutorial assumes that a user has prior knowledge on Virtual Private Cloud (VPC), Cloud Object Storage(COS), Key Protect Service and terraform.

In this article, I will show you how to provision an encrypted custom image in VPC Gen 2 using terraform.


The article assumes that the

1. user already have an account in IBM Cloud.
2. user has created an api key in his IBM Cloud account.
3. user has permission and quota to provision VPC resources and others in IBM Cloud.
4. user has installed terraform in his local system.

At the end of this article, the reader will get to know the steps to the provision the below resources in IBM Cloud services using Terraform:

(i) Cloud Object Storage Service
(ii) Key Protect Service
(iii) VPC custom image


About terraform:

Terraform is 'infrastructure as code' automation software by HashiCorp. It allows users to define cloud infrastructure in a high-level scripting language, which can be executed to build the infrastructure on the desired cloud platform.

For details on how to install and configure terraform, please refer the guide.

For more details on terraform and IBM Cloud, refer this link.

To try and test the below example, ensure that you have IBM Cloud Terraform Provider greater than v1.14.


About VPC Custom Image:

You can create your own custom image, and import it to IBM Cloud™ Virtual Private Cloud infrastructure from IBM Cloud Object Storage. Then, you can use your custom image to create new virtual server instances that run on the KVM hypervisor. When you have a qcow2 custom image that meets the requirements for IBM Cloud Virtual Private Cloud infrastructure, you can choose to encrypt it. This procedure describes how to encrypt your custom image with LUKS encryption by using QEMU and your own unique passphrase. After your image is encrypted, you wrap the passphrase with your customer root key (CRK) to create a wrapped (or encrypted) data encryption key that is stored with your image metadata when you import it to IBM Cloud VPC.

For more details on VPC Custom Image, please refer this link.

There are 5 REST APIs for VPC Custom Image :

First 4 apis are used in terraform resource block. 

1. Create a VPC Custom image with attributes (POST method)

encrypted_data_key - optional base 64 encoded passphrase
encryption_key - optional A reference to the root key that was used to wrap the data key. The CRN of the Key Protect Root Key or Hyper Protect Crypto Service Root Key for this resource.
href  location of Cloud Object Storage url like cos://bucket/file
name of the VPC custom image
operating_system - install the image with existing Operating Systems like "red-7-amd64", "centos-7-amd64"
resource_group  (if not specified, default resource group is used)


2. Update a VPC Custom image (PATCH method), following attributes can be updated.

name of the VPC custom image


3. Get a VPC custom image (GET method) with attribute

id - The custom image identifier


4. Delete a VPC custom image (DELETE method) with attribute

id - The custom image identifier


5. List all VPC Custom images (GET method). This api is used by terraform data block.


data "ibm_is_images" "test1" {  

You can find the list of REST APIs here.


  1. Steps to encrypt your custom image

    Follow the steps as mentioned in link.

    1. Login to your ubuntu VSI. Copy the custom image from your mac system to ubuntu, which you wish to encrypt.

    (base) Malars-MacBook-Pro-2:Downloads malark$ scp rhel-guest-image-7.0-20140930.0.x86_64.qcow2 root@5x.xx.xx.xxx:/root/malar/


    4% 19MB 2.5MB/s 02:37 ETA


    2. Login to ubuntu system and get the info about qcow2 image.


    root@ubuntu-vsi:~/malar# qemu-img info rhel-guest-image-7.0-20140930.0.x86_64.qcow2
    image: rhel-guest-image-7.0-20140930.0.x86_64.qcow2
    file format: qcow2
    virtual size: 10G (10737418240 bytes)
    disk size: 415M
    cluster_size: 65536
    Format specific information:
    compat: 0.10
    refcount bits: 16


    3. Create a new, empty qcow2 file of the exact same size and encrypt it with LUKS encryption. Use the passphrase that you choose, for example, “data-key”, to encrypt the file:


    qemu-img create –object secret,id=sec0,data=data-key -f qcow2 -o encrypt.format=luks,encrypt.key-secret=sec0 rhel-guest-image-7.0-encrypted.qcow2 10737418240


    4. Convert your image, rhel-guest-image-7.0-20140930.0.x86_64.qcow2 to the encrypted image, rhel-guest-image-7.0-encrypted.qcow2.


    root@ubuntu-vsi:~/malar# qemu-img convert –object secret,id=sec0,data=data-key –image-opts driver=qcow2,file.filename=rhel-guest-image-7.0-20140930.0.x86_64.qcow2 –target-image-opts driver=qcow2,encrypt.key-secret=sec0,file.filename=rhel-guest-image-7.0-encrypted.qcow2 -n -p


    5. Verify the encrypted image:


    root@ubuntu-vsi:~/malar# qemu-img info rhel-guest-image-7.0-encrypted.qcow2
    image: rhel-guest-image-7.0-encrypted.qcow2
    file format: qcow2
    virtual size: 10G (10737418240 bytes)
    disk size: 975M
    encrypted: yes
    cluster_size: 65536
    Format specific information:
    compat: 1.1
    lazy refcounts: false
    refcount bits: 16
    ivgen alg: plain64
    hash alg: sha256
    cipher alg: aes-256
    uuid: e8da41d0-6aa8-4697-a273-8d0922966aa9
    format: luks
    cipher mode: xts
    active: true
    iters: 1365878
    key offset: 4096
    stripes: 4000
    active: false
    key offset: 262144
    active: false
    key offset: 520192
    active: false
    key offset: 778240
    active: false
    key offset: 1036288
    active: false
    key offset: 1294336
    active: false
    key offset: 1552384
    active: false
    key offset: 1810432
    payload offset: 2068480
    master key iters: 343341
    corrupt: false


    6. Compare the two files to verify that they are identical.


    root@ubuntu-vsi:~/malar# qemu-img compare –object secret,id=sec0,data=data-key –image-opts driver=qcow2,file.filename=rhel-guest-image-7.0-20140930.0.x86_64.qcow2 driver=qcow2,encrypt.key-secret=sec0,file.filename=rhel-guest-image-7.0-encrypted.qcow2 -p
    Images are identical.

    7. Check the file for errors.


    root@ubuntu-vsi:~/malar# qemu-img check –object secret,id=sec0,data=data-key –image-opts driver=qcow2,encrypt.key-secret=sec0,file.filename=rhel-guest-image-7.0-encrypted.qcow2
    No errors were found on the image.
    15585/163840 = 9.51% allocated, 0.00% fragmented, 0.00% compressed clusters
    Image end offset: 1024000000


  2. Upload the encrypted image to IBM Cloud Object Storage associated with Key management Service.

    Follow the below steps and create 3 authorization policies as mentioned in link:

    1. From IBM Cloud Identity and Access Management (IAM), create an authorization between Cloud Block Storage (source service) and your key management service (target service) that is Key Protect Service. The authorization permits the IBM Cloud backplane services to use your WDEK for data encryption.

    2. Ensure that you have created an IAM authorization between the Image Service for VPC and IBM Cloud Object Storage.

    Specify Infrastructure Services as the source service. Specify Image Service for VPC as the resource type. Specify Cloud Object Storage as the target service. The authorization is so that the Image Service for VPC can access images in IBM Cloud Object Storage. For more information, see Granting access to IBM Cloud Object Storage to import images.

    3. Ensure that you have created an IAM authorization between the IBM Cloud Object Storage and key management service (target service) that is Key Protect Service.

    Create Services:

    Create a Key Protect Service in IBM Cloud and add a key of type “root key” in that service instance.

    Create a Cloud Object Service (COS) in IBM Cloud. Create a bucket and associate the bucket with Key Protect service and its “root key” as shown below:

    Note: The bucket region and Key Protect Service region should be same.



    Upload the encrypted image to COS bucket as shown below.

    Copy the encrypted image to your local system:

    (base) Malars-MacBook-Pro-2:Downloads malark$ scp root@5x.xx.xx.xxx:/root/malar/rhel-guest-image-7.0-encrypted.qcow2 .


    Upload the Image to COS.



  3. Wrap the data key with root key in Key Protect Service

    To import an encrypted custom image to IBM Cloud VPC, you must have a key management service provisioned. You also need a customer root key (CRK) and a wrapped data encryption key (WDEK). The WDEK is the passphrase that you used to encrypt your image wrapped with your CRK so that your passphrase remains known only to you. The WDEK is used to access the encrypted image when a virtual server instance that uses the encrypted image is started.

    We have already provisioned Key Management Service, Key Protect Service. Also, a root key is added in Key Protect Service. The root key is associated with COS. 

    Wrap (protect) the passphrase(we used “data-key” for encrypting qcow2 image) that you used to encrypt your image with your customer root key to create a wrapped data encryption key (WDEK).

    Install the plugins for Key Protect CLI by following the steps here.

    1. Open a terminal, Login to IBM Cloud. Select the account where your services are deployed.


    (base) Malars-MacBook-Pro-2:~ malark$ ibmcloud login -a https://cloud.ibm.com –sso
    API endpoint: https://cloud.ibm.com
    Region: us-south

    Get One Time Code from https://identity-3.eu-central.iam.cloud.ibm.com/identity/passcode to proceed.
    Open the URL in the default browser? [Y/n] > y
    One Time Code >


    2.  List all resources instances:


    (base) Malars-MacBook-Pro-2:~ malark$ ibmcloud resource service-instances
    Retrieving instances with type service_instance in all resource groups in all locations under account VNF Experiments Account as K.MALARVIZHI@IN.IBM.COM…


    3. Get the id of Key Protect Service using service name:


    (base) Malars-MacBook-Pro-2:~ malark$ ibmcloud resource service-instance “Key Protect-z9” –id
    Retrieving service instance Key Protect-z9 in all resource groups under account VNF Experiments Account as K.MALARVIZHI@IN.IBM.COM…
    crn:v1:bluemix:public:kms:us-south:a/……:<id of key protect service>:: <id of key protect service>


    4.Encode the passphrase:

    (base) Malars-MacBook-Pro-2:Downloads malark$ echo -n “data-key”|base64

    5. Wrap the encoded passphrase with root key in Key Protect Service. 

    (base) Malars-MacBook-Pro-2:Downloads malark$ ibmcloud kp key wrap <id of root key> -i <id of key protect instance> -p ZGF0YS1rZXk=
    Wrapping key…

    Use this Ciphertext to provision VPC Custom Image or import Custom Image in VPC.

  4. Initialize the terraform provider with your api key and cloud provider

    provider “ibm” {
    ibmcloud_api_key = “xxx-xxx”
    generation = 2
    region = “us-south”
    ibmcloud_timeout = 300

  5. Fetch the required resources from cloud using data block

    variable “region” {
    default = “us-south”
    description = “The VPC Region that you want your VPC, networks and the F5 virtual server to be provisioned in. To list available regions, run `ibmcloud is regions`.”

    variable “generation” {
    default = 2
    description = “The VPC Generation to target. Valid values are 2 or 1.”

    variable “resource_group” {
    default = “Default”
    description = “The resource group to use. If unspecified, the account’s default resource group is used.”

    # Read/validate Region
    data “ibm_is_region” “region” {
    name = var.region

    data “ibm_is_zone” “zone” {
    name = “us-south-1”
    region = data.ibm_is_region.region.name

    data “ibm_is_vpc” “malar_vpc” {
    name = “malar-vpc”

    data “ibm_is_ssh_key” “malar_ssh_key” {
    name = “malar-ssh-key”

    data “ibm_resource_group” “rg” {
    name = var.resource_group

    // source subnet 1
    data “ibm_is_subnet” “vnf_subnet” {
    name = “malar-f5-subnet”

  6. Create VPC Custom image resource for the uploaded image in Cloud Object Storage

    locals {
    rhel_image_url_enc = “cos://us-south/cos-enc-us-south/rhel-guest-image-7.0-encrypted.qcow2”

    # Generating random ID
    resource “random_uuid” “test” { }


    // Create/Read/Update/Delete resource block

    resource “ibm_is_image” “rhel_custom_image” {
    encrypted_data_key = “eyJjI….MzEyOSJ9”
    encryption_key = “crn”
    depends_on = [“random_uuid.test”]
    href = “${local.rhel_image_url_enc}”
    name = “rhel-enc-${substr(random_uuid.test.result,0,8)}”
    operating_system = “red-7-amd64”
    resource_group = “${data.ibm_is_subnet.vnf_subnet.resource_group}”


    // Create VSI using custom image

    resource “ibm_is_instance” “vsi1” {
    name = “vsi1”
    image = ibm_is_image.rhel_custom_image.id
    profile = “bx2-2×8”
    resource_group = data.ibm_resource_group.rg.id

    primary_network_interface {
    subnet = data.ibm_is_subnet.vnf_subnet.id
    security_groups = [ibm_is_security_group.cr_security_group.id] }
    keys = [data.ibm_is_ssh_key.malar_ssh_key.id] vpc = data.ibm_is_vpc.malar_vpc.id
    zone = “us-south-1”


    # create floating IP for vsi1

    resource “ibm_is_floating_ip” “floating_ip” {
    name = “rhel-vsi1”
    target = ibm_is_instance.vsi1.primary_network_interface.0.id


  7. Create Key Protect, Cloud Object Storage services and authorization policy using terraform

    // Key Protect Service

    //service instance for Key Protect

    resource “ibm_resource_instance” “kms_instance1” {
    name = “keyprotect-123”
    service = “kms”
    plan = “tiered-pricing”
    location = “us-south”

    // root key

    resource “ibm_kms_key” “test” {
    instance_id = “${ibm_resource_instance.kms_instance1.guid}”
    key_name = “my-root-key”
    standard_key = false
    force_delete = true

    // Authorization Policy for COS and Key Protect

    resource “ibm_iam_authorization_policy” “policy” {
    source_service_name = “cloud-object-storage”
    target_service_name = “kms”
    roles = [“Reader”] }

    // Cloud Object Storage Service

    //service instance for Cloud Object Storage Service

    resource “ibm_resource_instance” “instance2” {
    name = “cos-instance-test”
    resource_group_id = “${data.ibm_is_subnet.vnf_subnet.resource_group}”
    service = “cloud-object-storage”
    plan = “standard”
    location = “global”

    // COS bucket

    resource “ibm_cos_bucket” “bucket2” {
    depends_on = [ibm_iam_authorization_policy.policy] bucket_name = “us-south-bucket-enc-malar”
    resource_instance_id = ibm_resource_instance.instance2.id
    region_location = “us-south”
    storage_class = “standard”
    key_protect = ibm_kms_key.test.id

    // Authorization Policy for Block Storage and Key Protect

    resource “ibm_iam_authorization_policy” “policy1” {
    source_service_name = “server-protect”
    target_service_name = “kms”
    roles = [“Reader”] }


  8. Run terraform

     For creating the VPC resources using terraform,  run the commands below:

    // Initialize the IBM terraform provider

    terraform init


    // Create / Update resources in IBM Cloud
    terraform apply

    Open a terminal and ssh into the VSI using floating IP as shown below:


    (base) Malars-MacBook-Pro-2:~ malark$ ssh root@5x.xx.xxx.xxx
    The authenticity of host …. can’t be established.
    ECDSA key fingerprint is SHA256:………
    Are you sure you want to continue connecting (yes/no)? yes

  9. Destroy created resources in IBM Cloud

    For destroying the VPC resources created using terraform,  run the command below:

    terraform destroy

Join The Discussion