Provisioning Fedora CoreOS on Azure

This guide shows how to provision new Fedora CoreOS (FCOS) nodes on Azure. Fedora currently does not publish Fedora CoreOS images within Azure, so you must download an Azure image from Fedora and upload it to your Azure subscription.

FCOS does not support legacy Azure Service Manager virtual machines.

Prerequisites

Before provisioning an FCOS machine, you must have an Ignition configuration file containing your customizations. If you do not have one, see Producing an Ignition File.

Fedora CoreOS has a default core user that can be used to explore the OS. If you want to use it, finalize its configuration by providing e.g. an SSH key.

If you do not want to use Ignition to get started, you can make use of the Afterburn support.

You also need to have access to an Azure subscription. The examples below use the Azure CLI.

Downloading an Azure image

Fedora CoreOS is designed to be updated automatically, with different schedules per stream. Once you have picked the relevant stream, download, verify, and decompress the latest Azure image:

STREAM="stable"
coreos-installer download --decompress -s $STREAM -p azure -f vhd.xz

Alternatively, you can manually download an Azure image from the download page. Verify the download, following the instructions on that page, and decompress it.

Uploading the image to Azure

  1. Create any resources that don’t already exist in your Azure account:

    Example creating Azure resources
    az_region="westus2"
    az_resource_group="my-group"
    az_storage_account="mystorageacct"
    az_container="my-container"
    # Create resource group
    az group create -l "${az_region}" -n "${az_resource_group}"
    # Create storage account for uploading FCOS image
    az storage account create -g "${az_resource_group}" -n "${az_storage_account}"
    # Retrieve connection string for storage account
    cs=$(az storage account show-connection-string -n "${az_storage_account}" -g "${az_resource_group}" | jq -r .connectionString)
    # Create storage container for uploading FCOS image
    az storage container create --connection-string "${cs}" -n "${az_container}"
  2. Create an FCOS image:

    Example creating Azure image
    downloaded_image_file="./image.vhd"
    az_image_name="my-fcos-image"
    az_image_blob="${az_image_name}.vhd"
    # Upload image blob
    az storage blob upload --connection-string "${cs}" -c "${az_container}" -f "${downloaded_image_file}" -n "${az_image_blob}"
    # Create the image
    az image create -n "${az_image_name}" -g "${az_resource_group}" --source "https://${az_storage_account}.blob.core.windows.net/${az_container}/${az_image_blob}" --location "${az_region}" --os-type Linux
    # Delete the uploaded blob
    az storage blob delete --connection-string "$cs" -c "${az_container}" -n "${az_image_blob}"

Launching a VM instance using custom-data

  1. Launch a VM. Your Ignition configuration can be passed to the VM as custom data, or you can skip passing custom data if you just want SSH access. Your SSH public key from ~/.ssh will automatically be added to the VM. This provides an easy way to test out FCOS without first creating an Ignition config.

    Example launching Azure image
    az_vm_name="my-fcos-vm"
    ignition_path="./config.ign"
    az vm create -n "${az_vm_name}" -g "${az_resource_group}" --image "${az_image_name}" --admin-username core --custom-data "$(cat ${ignition_path})"
  2. You now should be able to SSH into the instance using the associated IP address.

    Example connecting
    ssh core@<ip address>

Launching a VM instance using custom-data and a private Azure blob

Define your variables
az_vm_name=my-fcos-vm
ignition_path="./config.ign"
az_blob_ignition_path=./privateConfig.ign
az_blob_ignition_file_name=privateConfig.ign
Upload your ign file to Azure blob storage
az storage blob upload --connection-string "${cs}" -c "${az_blob_ignition_file_name}" -f  "${az_blob_ignition_path}" -n "${ignition_file_name}"

+ . Create your remote ignition config to reference this new blob. Read about that here Using a remote Ignition config

+ . Create an identity and give it proper access to your storage account.

az identity create --name "${az_vm_name}-identity" --resource-group "${az_resource_group}"
identity_principal_id=$(az identity show --name "${az_vm_name}-identity" --resource-group "${az_resource_group}" --query principalId -o tsv)
identity_id=$(az identity show --name "${az_vm_name}-identity" --resource-group "${az_resource_group}" --query id -o tsv)

az role assignment create --assignee "${identity_principal_id}" --role "Storage Blob Data Contributor" --scope /subscriptions/${subscription_id}/resourceGroups/${az_resource_group}/providers/Microsoft.Storage/storageAccounts/${az_storage_account}

+ .Create the VM passing the new identity

az vm create -n "${az_vm_name}" -g "${az_resource_group}" --image "${az_image_name}" --admin-username core --custom-data "$(cat ${ignition_path})" --assign-identity "${identity_id}"

Launching a Confidential VM instance

Support for Confidential Computing is a work in progress in Fedora CoreOS. See the issue #1719.
For an overview about confidential VMs on Azure see confidential VM overview.

To launch a confidential VM, we need to create an image that supports confidential computing in an Azure Compute Gallery.

Example creating a gallery image that supports confidential computing
# Create an image gallery
gallery_name="mygallery"
az sig create --resource-group "${az_resource_group}" --gallery-name "${gallery_name}"

# Create a gallery image definition
gallery_image_definition="mygallery-def"
az sig image-definition create \
    --resource-group "${az_resource_group}" \
    --gallery-name "${gallery_name}" \
    --gallery-image-definition "${gallery_image_definition}" \
    --publisher azure \
    --offer example \
    --sku standard \
    --features SecurityType=ConfidentialVmSupported \
    --os-type Linux \
    --hyper-v-generation V2

# Get the source VHD URI of OS disk
os_vhd_storage_account=$(az storage account list -g ${az_resource_group} | jq -r .[].id)

# Create a new image version
gallery_image_version="1.0.0"
az sig image-version create \
    --resource-group "${az_resource_group}" \
    --gallery-name "${gallery_name}" \
    --gallery-image-definition "${gallery_image_definition}" \
    --gallery-image-version "${gallery_image_version}" \
    --os-vhd-storage-account "${os_vhd_storage_account}" \
    --os-vhd-uri https://${az_storage_account}.blob.core.windows.net/${az_container}/${az_image_blob}

To launch a confidential FCOS instance, you need to specify the confidential compute type and use a machine type that supports confidential computing.

From the command-line, use --security-type ConfidentialVM and --size.

Example launching a Confidential VM instance
vm_name="my-fcos-cvm"
ignition_path="./config.ign"

# Specify a size that supports confidential computing (using AMD SEV-SNP for example)
vm_size="Standard_DC2as_v5"

# Get gallery image id
gallery_image_id=$(az sig image-version show --gallery-image-definition "${gallery_image_definition}" --gallery-image-version "${gallery_image_version}" --gallery-name "${gallery_name}" --resource-group $az_resource_group | jq -r .id)

# Create a VM with confidential computing enabled using the gallery image and an ignition config as custom-data
az vm create \
    --name "${vm_name}" \
    --resource-group $az_resource_group \
    --size "${vm_size}" \
    --image "${gallery_image_id}" \
    --admin-username core \
    --generate-ssh-keys \
    --custom-data "$(cat ${ignition_path})" \
    --enable-vtpm true \
    --public-ip-sku Standard \
    --security-type ConfidentialVM \
    --os-disk-security-encryption-type VMGuestStateOnly \
    --enable-secure-boot true
We pass parameter --enable-secure-boot true to enable Secure Boot. Use false to disable secure boot.
To get the full console log, append the parameter --boot-diagnostics-storage ${az_storage_account}.
Example Confidential VM Boot Verification
ssh core@<ip address>
# Confirm the VM is using `AMD SEV-SNP` confidential type
sudo systemd-detect-virt --cvm
sev-snp

# Confirm the VM is using `Intel TDX` confidential type
sudo systemd-detect-virt --cvm
tdx

Note: Another way to confirm is looking at "Group B" and see that it ends with 2 (HV_ISOLATION_TYPE_SNP), or ends with 3 (HV_ISOLATION_TYPE_TDX).

Example Confidential VM Boot Verification by checking dmesg log
# `AMD SEV-SNP` confidential type
dmesg | grep "Hyper-V: Isolation Config"
[    0.000000] Hyper-V: Isolation Config: Group A 0x1, Group B 0xba2

# `Intel TDX` confidential type
dmesg | grep "Hyper-V: Isolation Config"
[    0.000000] Hyper-V: Isolation Config: Group A 0x1, Group B 0xbe3