Using official flatened kickstart to make an azure image

Dear,
I tried to use Rocky-9-Azure-LVM.ks in order to make an azure virtual machine.

Thus I create a script for this purpose below for reproducibility. When I create a VM with this image the deployment timeout (failed).
While using locally this vhd with qemu … that works.

How @nazunalika did you create the vhd file ?

below the script that do all in once

Thanks for your insight

#!/usr/bin/env bash
set -eE 
trap 'error_handler $? $LINENO' ERR

ISO_FILE='iso/Rocky-9.4-x86_64-minimal.iso'
KICKSTART_FILE=''
KICKSTART_OUTPUT_FILE=''
BASE_NAME='Custom Rockylinux'

RELEASE_VER='9'
RLTYPE='.4'
ARCH='x86_64'
MEM_SIZE=4096                       # Memory setting in MiB
VCPUS=1                             # CPU Cores count
OS_VARIANT='rocky'"${RELEASE_VER}"  # Select from the output of "osinfo-query os"
LOCATION="https://download.rockylinux.org/stg/rocky/${RELEASE_VER}/BaseOS/${ARCH}/os/"
REMOTE_ISO="https://download.rockylinux.org/pub/rocky/${RELEASE_VER}/isos/${ARCH}/Rocky-${RELEASE_VER}${RLTYPE}-${ARCH}-minimal.iso"
DISK_SIZE=11
DISK_FILE="./${BASE_NAME}.raw"
BRIDGE_NETWORK='bridge=virbr0'
STORAGE_ACCOUNT=''
RESOURCE_GROUP='rockylinux-rg'
SUBSCRIPTION_ID=''

# Fancy red-colored `error` function with `stderr` redirection with `exit`.
error (){
    { printf '\033[31;2;47m Error: %s \033[0m\n' "$*"; } >&2
}

error_handler(){
    error "($1) occurred on $2"
}

help(){
    echo "Usage: ${BASH_SOURCE[0]} [-h|--help] [-k|--kickstart filepath]"
    echo "Options:"
    echo "  -h, --help                  Show this help message and exit"
    echo "  -a, --account_key           Account key for the storage account"
    echo "  -k, --kickstart             Kickstart file path"
    echo "  -n, --name                  Base name used for others items such as image vm, vnet, subnet…"
    echo "  -o, --output                Result kickstart file path"
    echo "  -r, --resource-group        Resource group name to deploy image and virtual machine"
    echo "  -s, --storage_account       Name of the storage account"
    echo "      --subscription          Subscription ID used to locate image location"
}


does_args_value_is_missing(){
    local have_error
    have_error=false
    if [[ ! -n "$2" ]] || [[ ${2:0:1} == "-" ]]; then
        error "Argument for $1 is missing"
        have_error=true
    fi
    echo "${have_error}"
}

parse_cli(){
    local argument
    local have_error
    local args_value_is_missing
    local kickstart_filename
    have_error=false
    while (( "$#" )); do
        case "$1" in
            -h|--help)
                help
                exit 0
                ;;
            -a|--account_key)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    ACCOUNT_KEY="$2"
                    shift 2
                fi
                ;;
            -k|--kickstart)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    KICKSTART_FILE="$2"
                    shift 2
                    if [[ ! -e "${KICKSTART_FILE}" ]]; then
                        error "Kickstart: «${KICKSTART_FILE}» do not exist or is not readable!"
                        have_error=true
                    fi
                    if [[ ! -f "${KICKSTART_FILE}" ]]; then
                        error "Kickstart: «${KICKSTART_FILE}» is not a file!"
                        have_error=true
                    fi
                fi
                ;;
            -n|--name)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    BASE_NAME="$2"
                    DISK_FILE="./${BASE_NAME// /}.raw"
                    shift 2
                fi
                ;;
            -r|--resource-group)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    RESOURCE_GROUP="$2"
                    shift 2
                fi
                ;;
            -s|--storage_account)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    STORAGE_ACCOUNT="$2"
                    shift 2
                fi
                ;;
            --subscription)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    SUBSCRIPTION_ID="$2"
                    shift 2
                fi
                ;;
            -o|--output)
                args_value_is_missing=$(does_args_value_is_missing "$1" "$2")
                if ${args_value_is_missing}; then
                    have_error=true
                    shift
                else
                    KICKSTART_OUTPUT_FILE="$2"
                    shift 2
                fi
                ;;
            
            -*|--*=)
                # unsupported flags
                error "Unsupported flag $1"
                have_error=true
                shift
                ;;
            *)
                # preserve positional arguments
                error "Unsupported arguments $1"
                have_error=true
                shift
                ;;
        esac
    done
    if "${have_error}"; then
        help
        exit 1
    fi


    if [[ "${KICKSTART_OUTPUT_FILE}" == '' ]]; then
        kickstart_filename="$(basename "${KICKSTART_FILE}")"
        KICKSTART_OUTPUT_FILE='flatten_'"${kickstart_filename}"
    fi

    if [[ ! -f "${KICKSTART_FILE}" ]]; then
        error   "Kickstart File do not exists or permission issue!"
        exit 1
    fi


    echo "${arguments}"
}

main(){
    # Test for root privilege.
    if (( EUID != 0 )) ; then
        error "Expected root/sudoer user"
        exit 1
    fi
    set -x
    parse_cli "$@"    
    ksflatten -c "${KICKSTART_FILE}" -o "${KICKSTART_OUTPUT_FILE}"
    # Create ISO
    echo 'Creating ISO...'
    if [[ -e "${BASE_NAME}.iso" ]]; then
        rm "${BASE_NAME}.iso"
    fi
    mkksiso --cmdline "console=ttyS0,115200" \
            --rm "console" \
            --add chroot \
            "${ISO_FILE}" "${BASE_NAME}.iso"
            
    virt-install    --name "${BASE_NAME}" \
                    --memory="${MEM_SIZE}" \
                    --vcpus="${VCPUS}" \
                    --arch="${ARCH}" \
                    --disk="path=${DISK_FILE},size=${DISK_SIZE},format=raw" \
                    --network="${BRIDGE_NETWORK}" \
                    --graphics='none' \
                    --os-variant="${OS_VARIANT}" \
                    --location="${BASE_NAME}.iso" \
                    --noreboot \
                    --initrd-inject "${KICKSTART_OUTPUT_FILE}" \
                    --extra-args "inst.ks=file:/${KICKSTART_OUTPUT_FILE} console=ttyS0,115200"
    # Convert qcow2 to vhd
    # vhd image have to be 1 Mb aligned https://github.com/osbuild/osbuild-composer/issues/111#issuecomment-556008974
    # https://access.redhat.com/documentation/fr-fr/red_hat_enterprise_linux/9/html/deploying_rhel_9_on_microsoft_azure/convert-the-image_deploying-a-virtual-machine-on-microsoft-azure
    MB=$((1024*1024))
    cur_img_size=$(grep -Po 'virtual size:\s+\d+\s+\w+\s+\(\K\d+'  < <(qemu-img  info -f raw "${DISK_FILE}"))
    rounded_size="$(((cur_img_size/MB + 1) * MB))"
    if  [[ $(( cur_img_size % MB )) -eq 0 ]]; then
        qemu-img resize -f raw "${DISK_FILE}" ${rounded_size}
    fi
    qemu-img convert -f raw -o subformat=fixed,force_size -O vpc "${DISK_FILE}" "${BASE_NAME}.vhd"


    az storage container create --name          vhd \
                                --account-name  "${STORAGE_ACCOUNT}" \
                                --account-key   "${ACCOUNT_KEY}"

    az storage blob upload  --account-name   "${STORAGE_ACCOUNT}" \
                            --account-key "${ACCOUNT_KEY}"  \
                            --container-name vhd \
                            --file "${BASE_NAME}.vhd"  \
                            --overwrite
    
    az image create --resource-group "${RESOURCE_GROUP}" \
                    --name "${BASE_NAME}-img" \
                    --source "https://${STORAGE_ACCOUNT}.blob.core.windows.net/vhd/${BASE_NAME}.vhd" \
                    --hyper-v-generation V2 \
                    --os-type Linux
    
    az vm create --name "${BASE_NAME}-vm" \
                 --resource-group "${RESOURCE_GROUP}" \
                 --image "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Compute/images/${BASE_NAME}-img" \
                 --vnet-name ${BASE_NAME}-vnet \
                 --subnet ${BASE_NAME}-subnet \
                 --size Standard_DS2_v2  \
                 --generate-ssh-keys 
}

main "$@"   

We originally used image factory which does not involve some of the steps in that script. However, image factory is not recommended.

You would be better off making something similar via osbuild or using kiwi.

Thanks you for your insight I go take a look.
I wish you a good day

PS: the error message is

{"status":"Failed","error":{"code":"DeploymentFailed","target":"/subscriptions/xxxxxxxxx/resourceGroups/Test-custom-linux-image/providers/Microsoft.Resources/deployments/vm_deploy_crvKDc4MCY3ov54gdjiweikZisGF9umr","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.","details":[{"code":"ResourceDeploymentFailure","target":"/subscriptions/xxxx/resourceGroups/Test-custom-linux-image/providers/Microsoft.Compute/virtualMachines/rocky-vm","message":"The resource write operation failed to complete successfully, because it reached terminal provisioning state 'Failed'.","details":[{"code":"OSProvisioningTimedOut","message":"OS Provisioning for VM 'rocky-vm' did not finish in the allotted time. The VM may still finish provisioning successfully. Please check provisioning state later. Also, make sure the image has been properly prepared (generalized).\r\n * Instructions for Windows: https://azure.microsoft.com/documentation/articles/virtual-machines-windows-upload-image/ \r\n * Instructions for Linux: https://azure.microsoft.com/documentation/articles/virtual-machines-linux-capture-image/ \r\n * If you are deploying more than 20 Virtual Machines concurrently, consider moving your custom image to shared image gallery. Please refer to https://aka.ms/movetosig for the same."}]}]}}