Inicio rápido: implementación de una máquina virtual de Azure desde una plantilla con el SDK de Azure para Go

En esta guía de inicio rápido se muestra cómo implementar recursos desde una plantilla de Azure Resource Manager, mediante Azure SDK para Go. Las plantillas son instantáneas de todos los recursos dentro de un grupo de recursos de Azure. A lo largo de la guía, se irá familiarizado con la funcionalidad y las convenciones del SDK.

Al final de este inicio rápido, tendrá una máquina virtual en ejecución en la que podrá iniciar sesión con un nombre de usuario y una contraseña.

Nota:

Para ver cómo crear una máquina virtual en Go sin una plantilla de Resource Manager, hay un ejemplo imperativo que muestra cómo crear y configurar todos los recursos de máquina virtual con el SDK. Usar una plantilla de este ejemplo permite centrarse en las convenciones del SDK sin entrar en demasiados detalles acerca de la arquitectura del servicio de Azure.

Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

Inicio de Azure Cloud Shell

Azure Cloud Shell es un shell interactivo gratuito que se ejecuta en Azure. Tiene las herramientas comunes preinstaladas y configuradas para usarlas con su cuenta. Seleccione Copiar para copiar el código, péguelo en Cloud Shell y presione Entrar para ejecutarlo.

Hay unas cuantas maneras de iniciar Cloud Shell:

Seleccione Pruébelo en la esquina superior derecha de un bloque de código.

Cloud Shell in this article

Abra Cloud Shell en el explorador.

https://shell.azure.com/bash

Seleccione el botón Cloud Shell del menú situado en la parte superior derecha de Azure Portal.

Cloud Shell in the portal

Si usa una instalación local de la CLI de Azure, esta plantilla de inicio rápido requiere la versión 2.0.28 de la CLI o versiones posteriores. Ejecute az --version para asegurarse de que la instalación de la CLI cumple este requisito. Si necesita instalarla o actualizarla, consulte Instalación de la CLI de Azure.

Instalación del SDK de Azure para Go

Azure SDK para Go es compatible con las versiones de Go 1.8 y posteriores. Para entornos con perfiles de Azure Stack, la versión 1.9 de Go es el requisito mínimo. Si tiene que instalar Go, siga las instrucciones de instalación de Go.

Puede descargar Azure SDK para Go y sus dependencias en go get.

go get -u -d github.com/Azure/azure-sdk-for-go/...

Advertencia

Asegúrese de escribir en mayúsculas Azure en la dirección URL. De lo contrario puede causar problemas de importación relacionados con las mayúsculas y minúsculas cuando se trabaja con el SDK. También debe escribir en mayúscula Azure en las instrucciones de importación.

Creación de una entidad de servicio

Para iniciar sesión de una manera no interactiva en Azure con una aplicación, necesita una entidad de servicio. Las entidades de servicio son parte del control de acceso basado en rol (RBAC), que crea una identidad de usuario única. Para crear una nueva entidad de servicio con la CLI, ejecute el comando siguiente:

az ad sp create-for-rbac --role Contributor \
    --scopes /subscriptions/<subscription_id> \
    --sdk-auth > quickstart.auth

Establezca la variable de entorno AZURE_AUTH_LOCATION para que sea la ruta de acceso completa de este archivo. Después, el SDK busca y lee las credenciales directamente desde este archivo, sin que tenga que realizar cambios ni registrar información de la entidad de servicio.

Obtención del código

Obtenga el código de inicio rápido y todas sus dependencias con go get.

go get -u -d github.com/Azure-Samples/azure-sdk-for-go-samples/quickstarts/deploy-vm/...

No es necesario realizar ninguna modificación en el código fuente si la variable AZURE_AUTH_LOCATION está establecida correctamente. Cuando se ejecuta el programa, carga toda la información de autenticación necesaria desde allí.

Ejecución del código

Puede ejecutar el inicio rápido con el comando go run.

cd $GOPATH/src/github.com/Azure-Samples/azure-sdk-for-go-samples/quickstarts/deploy-vm
go run main.go

Si la implementación se realiza correctamente, verá un mensaje que proporciona el nombre de usuario, la dirección IP y la contraseña para iniciar sesión en la máquina virtual recién creada. Conéctese mediante SSH con este equipo para confirmar que se está ejecutando.

Limpieza

Para limpiar los recursos creados en este inicio rápido, puede eliminar el grupo de recursos con la CLI.

az group delete -n GoVMQuickstart

También elimina la entidad de servicio que se creó. En el archivo quickstart.auth, hay una clave JSON para clientId. Copie este valor en la variable de entorno CLIENT_ID_VALUE y ejecute el siguiente comando de la CLI de Azure:

az ad sp delete --id ${CLIENT_ID_VALUE}

Donde proporciona el valor para CLIENT_ID_VALUE desde quickstart.auth.

Advertencia

Si no se elimina la entidad de servicio de esta aplicación, la deja activa en el inquilino de Microsoft Entra. Aunque tanto el nombre como la contraseña de la entidad de servicio se generan como UUID, asegúrese de seguir los procedimientos de seguridad recomendados mediante la eliminación de las entidades de servicio sin usar y las aplicaciones de Microsoft Entra.

El código en profundidad

El contenido del código de inicio rápido se desglosa en un bloque de variables y varias funciones pequeñas, cada uno de los cuales se trata aquí.

Variables, constantes y tipos

Puesto que la plantilla de inicio rápido es independiente, usa variables y constantes globales.

const (
    resourceGroupName     = "GoVMQuickstart"
    resourceGroupLocation = "eastus"

    deploymentName = "VMDeployQuickstart"
    templateFile   = "vm-quickstart-template.json"
    parametersFile = "vm-quickstart-params.json"
)

// Information loaded from the authorization file to identify the client
type clientInfo struct {
    SubscriptionID string
    VMPassword     string
}

var (
    ctx        = context.Background()
    clientData clientInfo
    authorizer autorest.Authorizer
)

Los valores declarados proporcionan los nombres de los recursos creados. La ubicación también se especifica aquí y puede cambiarla para ver cómo se comportan las implementaciones en otros centros de datos. No todos los centros de datos tienen todos los recursos necesarios disponibles.

El tipo clientInfo guarda toda la información que se carga desde el archivo de autenticación para configurar los clientes en el SDK y establecer la contraseña de la máquina virtual.

Las constantes templateFile y parametersFile apuntan a los archivos necesarios para la implementación. El SDK para Go configurará authorizer para autenticación, y la variable ctx es un contexto de Go para las operaciones de red.

Autenticación e inicialización

La función init establece la autenticación. Puesto que la autenticación es una condición previa para todo en la plantilla de inicio rápido, tiene sentido que forme parte de la inicialización. También carga alguna información necesaria desde el archivo de autenticación para configurar los clientes y la máquina virtual.

func init() {
    var err error
    authorizer, err = auth.NewAuthorizerFromFile(azure.PublicCloud.ResourceManagerEndpoint)
    if err != nil {
        log.Fatalf("Failed to get OAuth config: %v", err)
    }

    authInfo, err := readJSON(os.Getenv("AZURE_AUTH_LOCATION"))
    clientData.SubscriptionID = (*authInfo)["subscriptionId"].(string)
    clientData.VMPassword = (*authInfo)["clientSecret"].(string)
}

En primer lugar, se invoca a auth.NewAuthorizerFromFile para cargar la información de autenticación del archivo se encuentra en AZURE_AUTH_LOCATION. Después, la función readJSON carga manualmente este archivo (se omite aquí) para extraer los dos valores necesarios para ejecutar el resto del programa: el identificador de la suscripción del cliente y secreto de la entidad de servicio, que también se utiliza para la contraseña de la máquina virtual.

Advertencia

Para simplificar el inicio rápido, se reutiliza la contraseña de la entidad de servicio. En producción, no olvide que nunca se reutiliza una contraseña que da acceso a los recursos de Azure.

Flujo de operaciones en main()

La función main es simple, solo indica el flujo de operaciones y realiza la comprobación de errores.

func main() {
    group, err := createGroup()
    if err != nil {
        log.Fatalf("failed to create group: %v", err)
    }
    log.Printf("Created group: %v", *group.Name)

    log.Printf("Starting deployment: %s", deploymentName)
    result, err := createDeployment()
    if err != nil {
        log.Fatalf("Failed to deploy: %v", err)
    }
    if result.Name != nil {
        log.Printf("Completed deployment %v: %v", deploymentName, *result.Properties.ProvisioningState)
    } else {
        log.Printf("Completed deployment %v (no data returned to SDK)", deploymentName)
    }
    getLogin()
}

Los pasos que ejecuta el código son, en orden:

  • Crear el grupo de recursos para la implementación (createGroup)
  • Crear la implementación dentro del grupo (createDeployment)
  • Obtener y mostrar la información de inicio de sesión de la máquina virtual implementada (getLogin)

Crear el grupo de recursos

La función createGroup crea el grupo de recursos. Un examen del flujo de llamadas y los argumentos muestra la forma en la que se estructuran las interacciones del servicio en el SDK.

func createGroup() (group resources.Group, err error) {
    groupsClient := resources.NewGroupsClient(clientData.SubscriptionID)
    groupsClient.Authorizer = authorizer

        return groupsClient.CreateOrUpdate(
                ctx,
                resourceGroupName,
                resources.Group{
                        Location: to.StringPtr(resourceGroupLocation)})
}

El flujo general de la interacción con un servicio de Azure es:

  • Crear el cliente mediante el método service.New*Client(), donde * es el tipo de recurso de service con el que desea interactuar. Esta función siempre tiene un identificador de suscripción.
  • Establecer el método de autorización para el cliente, lo que le permite interactuar con la API remota.
  • Realizar la llamada al método en el cliente correspondiente a la API remota. Los métodos del cliente del servicio normalmente toman el nombre del recurso y un objeto de metadatos.

La función to.StringPtr se utiliza aquí para realizar una conversión de tipo. Los parámetros de los métodos del SDK toman casi exclusivamente punteros, por lo que estos métodos se proporcionan para facilitar las conversiones de tipos. Consulte la documentación del módulo autorest/to para obtener la lista completa y el comportamiento de los convertidores.

El método groupsClient.CreateOrUpdate devuelve un puntero a un tipo de datos que representa al grupo de recursos. Un valor de retorno directo de este tipo indica una operación de ejecución breve que está diseñada para ser sincrónica. En la siguiente sección, verá un ejemplo de una operación de ejecución prolongada y cómo interactuar con ella.

Implementación

Una vez creado el grupo de recursos, es el momento de ejecutar la implementación. Este código se divide en secciones más pequeñas para hacer énfasis en las diferentes partes de su lógica.

func createDeployment() (deployment resources.DeploymentExtended, err error) {
    template, err := readJSON(templateFile)
    if err != nil {
        return
    }
    params, err := readJSON(parametersFile)
    if err != nil {
        return
    }
    (*params)["vm_password"] = map[string]string{
        "value": clientData.VMPassword,
    }
        // ...

readJSON carga los archivos de la implementación, los detalles de esta operación se omiten aquí. Esta función devuelve un valor del tipo *map[string]interface{}, el tipo utilizado para construir los metadatos de la llamada de la implementación de los recursos. La contraseña de la máquina virtual también se establece manualmente en los parámetros de implementación.

        // ...

    deploymentsClient := resources.NewDeploymentsClient(clientData.SubscriptionID)
    deploymentsClient.Authorizer = authorizer

    deploymentFuture, err := deploymentsClient.CreateOrUpdate(
        ctx,
        resourceGroupName,
        deploymentName,
        resources.Deployment{
            Properties: &resources.DeploymentProperties{
                Template:   template,
                Parameters: params,
                Mode:       resources.Incremental,
            },
        },
    )
    if err != nil {
        return
    }

Este código sigue el mismo patrón que la creación del grupo de recursos. Se crea un nuevo cliente, se le habilita para autenticarse con Azure y, a continuación, se llama a un método. El método incluso tiene el mismo nombre (CreateOrUpdate) que el método correspondiente en los grupos de recursos. Este patrón se ve en todo el SDK. Los métodos que realizan un trabajo similar normalmente tienen el mismo nombre.

La principal diferencia está en el valor devuelto por el método deploymentsClient.CreateOrUpdate. Este valor es un objeto del tipo Future, que sigue el patrón de diseño de futuros. Los futuros representan una operación de ejecución prolongada en Azure que puede sondear, cancelar o bloquear cuando finalice.

        //...
    err = deploymentFuture.Future.WaitForCompletion(ctx, deploymentsClient.BaseClient.Client)
    if err != nil {
        return
    }
    return deploymentFuture.Result(deploymentsClient)
}

En este ejemplo, lo mejor es esperar a que finalice la operación. Para esperar a un futuro se necesita tanto un objeto de contexto como el cliente que creó el objeto Future. Hay dos orígenes de error posibles aquí: un error producido en el cliente cuando se intenta invocar el método y una respuesta de error desde el servidor. El último se devuelve como parte de la llamada a deploymentFuture.Result.

Obtención de la dirección IP asignada

Para cualquier operación con la máquina virtual recién creada, se necesita la dirección IP asignada. Las direcciones IP son recursos de Azure independientes, enlazados a los recursos del controlador de interfaz de red (NIC).

func getLogin() {
    params, err := readJSON(parametersFile)
    if err != nil {
        log.Fatalf("Unable to read parameters. Get login information with `az network public-ip list -g %s", resourceGroupName)
    }

    addressClient := network.NewPublicIPAddressesClient(clientData.SubscriptionID)
    addressClient.Authorizer = authorizer
    ipName := (*params)["publicIPAddresses_QuickstartVM_ip_name"].(map[string]interface{})
    ipAddress, err := addressClient.Get(ctx, resourceGroupName, ipName["value"].(string), "")
    if err != nil {
        log.Fatalf("Unable to get IP information. Try using `az network public-ip list -g %s", resourceGroupName)
    }

    vmUser := (*params)["vm_user"].(map[string]interface{})

    log.Printf("Log in with ssh: %s@%s, password: %s",
        vmUser["value"].(string),
        *ipAddress.PublicIPAddressPropertiesFormat.IPAddress,
        clientData.VMPassword)
}

Este método se basa en la información que se almacena en el archivo de parámetros. El código podría consultar directamente la máquina virtual para obtener la NIC, consultar la NIC para obtener el recurso de IP y, finalmente, consultar el recurso de IP directamente. Es una cadena larga de dependencias y operaciones para resolver, lo que resulta costoso. Puesto que la información de JSON es local, se puede cargar en su lugar.

El valor del usuario de la máquina virtual también se carga desde el código JSON. La contraseña de la máquina virtual se cargó anteriormente desde el archivo de autenticación.

Pasos siguientes

En este inicio rápido, ha tomado una plantilla existente para su implementación mediante Go. Después se ha conectado mediante SSH a la máquina virtual recién creada.

Para seguir obteniendo información acerca de cómo trabajar con máquinas virtuales en el entorno de Azure con Go, puede consultar los ejemplos de proceso de Azure para Go o los ejemplos de administración de recursos de Azure para Go.

Para obtener más información acerca de los métodos de autenticación disponibles en el SDK y qué tipos de autenticación se admiten, consulte Autenticación con el SDK de Azure para Go.