Exemple #1
0
def register(registration_endpoint, worker_group_name, machine_id, cert_path,
             key_path, is_azure_vm, vm_id, azure_resource_id, test_mode):
    """Registers the worker through the automation linked account with the Agent Service.

    Returns:
        The deserialized response from the Agent Service.
    """
    http_client_factory = httpclientfactory.HttpClientFactory(
        cert_path, key_path, test_mode)
    http_client = http_client_factory.create_http_client(sys.version_info)

    no_proxy_http_client_factory = httpclientfactory.HttpClientFactory(
        cert_path, key_path, test_mode, force_no_proxy=True)
    no_proxy_http_client = no_proxy_http_client_factory.create_http_client(
        sys.version_info)

    headers, payload = get_headers_and_payload(worker_group_name, is_azure_vm,
                                               vm_id, azure_resource_id,
                                               cert_path, no_proxy_http_client)
    url = registration_endpoint + "/HybridV2(MachineId='" + machine_id + "')"

    response = http_client.put(url, headers=headers, data=payload)

    if response.status_code != 200:
        raise Exception("Unable to register [status_code=" +
                        str(response.status_code) + "]")

    return json.loads(response.raw_data), payload
def deregister(registration_endpoint, worker_group_name, machine_id, cert_path, key_path, test_mode):
    """Deregisters the worker through the automation linked account with the Agent Service.

    Note:
        This method is only present for testing purposes for now. Linked account deregistration is not yet implemented
        and deregistration need to be made through using the automation account information.

    Returns:

    """
    headers, payload = get_headers_and_payload(worker_group_name, certificate_path=cert_path)
    url = registration_endpoint + "/Hybrid(MachineId='" + machine_id + "')"

    http_client_factory = httpclientfactory.HttpClientFactory(cert_path, key_path, test_mode)
    http_client = http_client_factory.create_http_client(sys.version_info)
    response = http_client.delete(url, headers=headers, data=payload)

    if response.status_code != 200:
        raise Exception("Unable to deregister [status_code=" + str(response.status_code) + "]")
def deregister(options):
    registration_endpoint = options.registration_endpoint
    automation_account_key = options.automation_account_key
    workspace_id = options.workspace_id

    # assert workspace exists on the box
    state_base_path = "/var/opt/microsoft/omsagent/" + workspace_id + "/state/"
    working_directory_base_path = "/var/opt/microsoft/omsagent/" + workspace_id + "/run/"
    if os.path.exists(state_base_path) is False or os.path.exists(working_directory_base_path) is False:
        raise Exception("Invalid workspace id. Is the specified workspace id registered as the OMSAgent "
                        "primary worksapce?")

    diy_state_base_path = os.path.join(state_base_path, os.path.join("automationworker", "diy"))
    diy_working_directory_base_path = os.path.join(working_directory_base_path, os.path.join("automationworker", "diy"))
    worker_conf_path = os.path.join(diy_state_base_path, "worker.conf")
    certificate_path = os.path.join(diy_state_base_path, "worker_diy.crt")
    key_path = os.path.join(diy_state_base_path, "worker_diy.key")

    if os.path.exists(worker_conf_path) is False:
        raise Exception("Unable to deregister, no worker configuration found on disk.")

    if os.path.exists(certificate_path) is False or os.path.exists(key_path) is False:
        raise Exception("Unable to deregister, no worker certificate/key found on disk.")

    issuer, subject, thumbprint = linuxutil.get_cert_info(certificate_path)

    if os.path.exists(worker_conf_path) is False:
        raise Exception("Missing worker configuration.")

    if os.path.exists(certificate_path) is False:
        raise Exception("Missing worker certificate.")

    if os.path.exists(key_path) is False:
        raise Exception("Missing worker key.")

    config = ConfigParser.ConfigParser()
    config.read(worker_conf_path)
    machine_id = config.get("worker-required", "machine_id")

    # generate payload for registration request
    date = datetime.datetime.utcnow().isoformat() + "0-00:00"
    payload = {"Thumbprint": thumbprint,
               "Issuer": issuer,
               "Subject": subject}

    # the signature generation is based on agent service contract
    payload_hash = sha256_digest(payload)
    b64encoded_payload_hash = base64.b64encode(payload_hash)
    signature = generate_hmac(b64encoded_payload_hash + "\n" + date, automation_account_key)
    b64encoded_signature = base64.b64encode(signature)

    headers = {'Authorization': 'Shared ' + b64encoded_signature,
               'ProtocolVersion': "2.0",
               'x-ms-date': date,
               "Content-Type": "application/json"}

    # agent service registration request
    http_client_factory = httpclientfactory.HttpClientFactory(certificate_path, key_path, options.test)
    http_client = http_client_factory.create_http_client(sys.version_info)
    url = registration_endpoint + "/Hybrid(MachineId='" + machine_id + "')"
    response = http_client.delete(url, headers=headers, data=payload)

    if response.status_code != 200:
        raise Exception("Failed to deregister worker. [response_status=" + str(response.status_code) + "]")
    if response.status_code == 404:
        raise Exception("Unable to deregister. Worker not found.")
    print "Successfuly deregistered worker."

    print "Cleaning up left over directories."

    try:
        shutil.rmtree(diy_state_base_path)
        print "Removed state directory."
    except:
        raise Exception("Unable to remove state directory base path.")

    try:
        shutil.rmtree(diy_working_directory_base_path)
        print "Removed working directory."
    except:
        raise Exception("Unable to remove working directory base path.")
               "VirtualMachineId": vm_id,
               "Subject": subject}

    # the signature generation is based on agent service contract
    payload_hash = sha256_digest(payload)
    b64encoded_payload_hash = base64.b64encode(payload_hash)
    signature = generate_hmac(b64encoded_payload_hash + "\n" + date, automation_account_key)
    b64encoded_signature = base64.b64encode(signature)

    headers = {'Authorization': 'Shared ' + b64encoded_signature,
               'ProtocolVersion': "2.0",
               'x-ms-date': date,
               "Content-Type": "application/json"}

    # agent service registration request
    http_client_factory = httpclientfactory.HttpClientFactory(certificate_path, key_path, options.test)
    http_client = http_client_factory.create_http_client(sys.version_info)
    url = registration_endpoint + "/HybridV2(MachineId='" + machine_id + "')"
    response = http_client.put(url, headers=headers, data=payload)

    if response.status_code != 200:
        raise Exception("Failed to register worker. [response_status=" + str(response.status_code) + "]")

    registration_response = json.loads(response.raw_data)
    account_id = registration_response["AccountId"]
    create_worker_configuration_file(registration_response["jobRuntimeDataServiceUri"], account_id,
                                     hybrid_worker_group_name, machine_id, diy_working_directory_base_path,
                                     diy_state_base_path, certificate_path, key_path, registration_endpoint,
                                     workspace_id, thumbprint, vm_id, is_azure_vm, options.test)

    # generate working directory path
Exemple #5
0
def register(options):
    environment_prerequisite_validation()
    """Registers the machine against the automation agent service.

    Args:
        options : dict, the options dictionary
    """
    registration_endpoint = options.registration_endpoint
    automation_account_key = options.automation_account_key
    hybrid_worker_group_name = options.hybrid_worker_group_name
    workspace_id = options.workspace_id

    # assert workspace exists on the box
    state_base_path = "/var/opt/microsoft/omsagent/" + workspace_id + "/state/"
    working_directory_base_path = "/var/opt/microsoft/omsagent/" + workspace_id + "/run/"
    if os.path.exists(state_base_path) is False or os.path.exists(
            working_directory_base_path) is False:
        raise Exception(
            "Invalid workspace id. Is the specified workspace id registered as the OMSAgent "
            "primary worksapce?")

    diy_account_id = extract_account_id_from_registration_endpoint(
        registration_endpoint)
    auto_registered_account_id = get_autoregistered_worker_account_id()
    if auto_registered_account_id != None and auto_registered_account_id != diy_account_id:
        raise Exception(
            "Cannot register, conflicting worker already registered.")

    worker_conf_path = os.path.join(DIY_STATE_PATH, "worker.conf")

    if os.path.isfile(worker_conf_path) is True:
        raise Exception(
            "Unable to register, an existing worker was found. Please deregister any existing worker and "
            "try again.")

    certificate_path = os.path.join(DIY_STATE_PATH, "worker_diy.crt")
    key_path = os.path.join(DIY_STATE_PATH, "worker_diy.key")
    machine_id = util.generate_uuid()

    # generate state path (certs/conf will be dropped in this path)
    if os.path.isdir(DIY_STATE_PATH) is False:
        try:
            os.makedirs(DIY_STATE_PATH)
        except Exception as ex:
            print("Registration unsuccessful.")
            print(
                "Cannot create directory for certs/conf. Because of the following exception : "
                + str(ex))
            return
    generate_self_signed_certificate(certificate_path=certificate_path,
                                     key_path=key_path)
    issuer, subject, thumbprint = linuxutil.get_cert_info(certificate_path)

    # try to extract optional metadata
    unknown = "Unknown"
    asset_tag = unknown
    vm_id = unknown
    is_azure_vm = False
    try:
        dmidecode = invoke_dmidecode()
        is_azure_vm = linuxutil.is_azure_vm(dmidecode)
        if is_azure_vm:
            asset_tag = linuxutil.get_azure_vm_asset_tag()
        else:
            asset_tag = False
        vm_id = linuxutil.get_vm_unique_id_from_dmidecode(
            sys.byteorder, dmidecode)
    except Exception as e:
        print(str(e))
        pass

    # generate payload for registration request
    date = datetime.datetime.utcnow().isoformat() + "0-00:00"
    payload = {
        'RunbookWorkerGroup': hybrid_worker_group_name,
        "MachineName": socket.gethostname().split(".")[0],
        "IpAddress": get_ip_address(),
        "Thumbprint": thumbprint,
        "Issuer": issuer,
        "OperatingSystem": 2,
        "SMBIOSAssetTag": asset_tag,
        "VirtualMachineId": vm_id,
        "Subject": subject
    }

    # the signature generation is based on agent service contract
    payload_hash = sha256_digest(payload)
    b64encoded_payload_hash = base64.b64encode(payload_hash)
    signature = generate_hmac(
        b64encoded_payload_hash.decode("utf-8") + "\n" + date,
        automation_account_key)
    b64encoded_signature = base64.b64encode(signature)

    headers = {
        'Authorization': 'Shared ' + b64encoded_signature.decode("utf-8"),
        'ProtocolVersion': "2.0",
        'x-ms-date': date,
        "Content-Type": "application/json"
    }
    is_conf_file_writable = check_if_conf_file_can_be_written()

    if is_conf_file_writable:
        # agent service registration request
        http_client_factory = httpclientfactory.HttpClientFactory(
            certificate_path, key_path, options.test)
        http_client = http_client_factory.create_http_client(sys.version_info)
        url = registration_endpoint + "/HybridV2(MachineId='" + machine_id + "')"
        response = http_client.put(url, headers=headers, data=payload)
        if response.status_code != 200:
            raise Exception("Failed to register worker. [response_status=" +
                            str(response.status_code) + "]")

        response.raw_data = response.raw_data.decode() if isinstance(
            response.raw_data, bytes) else response.raw_data
        registration_response = json.loads(response.raw_data)
        account_id = registration_response["AccountId"]
        create_worker_configuration_file(
            registration_response["jobRuntimeDataServiceUri"], account_id,
            hybrid_worker_group_name, machine_id, DIY_WORKING_DIR,
            DIY_STATE_PATH, certificate_path, key_path, registration_endpoint,
            workspace_id, thumbprint, vm_id, is_azure_vm, options.gpg_keyring,
            options.test)

        # generate working directory path
        diydirs.create_persistent_diy_dirs()

        print("Registration successful!")
    else:
        print(
            "Registration cannot be completed because configuration file could not be written. Please check the file permissions for /home/nxautomation folder"
        )
def main(argv):
    agent_id = None
    is_azure_vm = False
    vm_id = None
    oms_cert_path = None
    oms_key_path = None
    endpoint = None
    gpg_keyring_path = None
    operation = None
    proxy_configuration_path = None
    test_mode = False
    state_directory = None
    working_directory = None
    workspace_id = None
    mock_powershelldsc_test = False
    diy_account_id = None
    azure_resource_id = None

    # parse cmd line args
    try:
        opts, args = getopt.getopt(argv, "hrdw:a:c:k:e:f:s:p:g:y:i:v:zt", [
            "help", "register", "deregister", "workspaceid=", "agentid=",
            "certpath=", "keypath=", "endpoint=", "workingdirpath=",
            "statepath=", "proxyconfpath=", "gpgkeyringpath=", "diyaccountid=",
            "mock_powershelldsc_test=", "vmid=", "azureresourceid="
        ])
    except getopt.GetoptError:
        print __file__ + "[--register, --deregister] -w <workspaceid> -a <agentid> -c <certhpath> -k <keypath> " \
                         "-e <endpoint> -f <workingdirpath> -s <statepath> -p <proxyconfpath> -g <gpgkeyringpath>" \
                         "-y <diyaccountid> -i <vmid>"
        sys.exit(2)
    for opt, arg in opts:
        if opt == ("-h", "--help"):
            print __file__ + "[--register, --deregister] -w <workspaceid> -a <agentid> -c <certhpath> -k <keypath> " \
                             "-e <endpoint> -f <workingdirpath> -s <statepath> -p <proxyconfpath> -g <gpgkeyringpath>" \
                             "-y <diyaccountid> -i <vmid>"
            sys.exit()
        elif opt in ("-r", "--register"):
            operation = REGISTER
        elif opt in ("-d", "--deregister"):
            operation = DEREGISTER
        elif opt in ("-w", "--workspaceid"):
            workspace_id = arg.strip()
        elif opt in ("-a", "--agentid"):
            agent_id = arg.strip()
        elif opt in ("-c", "--certpath"):
            oms_cert_path = arg.strip()
        elif opt in ("-k", "--keypath"):
            oms_key_path = arg.strip()
        elif opt in ("-e", "--endpoint"):
            endpoint = arg.strip()
        elif opt in ("-f", "--workingdirpath"):
            working_directory = arg.strip()
        elif opt in ("-p", "--proxyconfpath"):
            proxy_configuration_path = arg.strip()
        elif opt in ("-s", "--statepath"):
            state_directory = arg.strip()
        elif opt in ("-g", "--gpgkeyringpath"):
            gpg_keyring_path = arg.strip()
        elif opt in ("-y", "--diyaccountid"):
            diy_account_id = arg.strip()
        elif opt in ("-z", "--azurevm"):
            is_azure_vm = True
        elif opt in ("-v", "--azureresourceid"):
            azure_resource_id = arg.strip(
            )  # Use the Resource ID from DSC resource as a backup. Overwrite it with metadata from IMDS when available
        elif opt in ("-i", "--vmid"):
            vm_id = arg.strip(
            )  # Use the VM ID from DSC resource as a backup. Overwrite it with metadata from IMDS when available
        elif opt in ("-t", "--test"):
            test_mode = True
        elif opt == "--mock_powershelldsc_test":
            # generate a dummy configuration file
            # does not do actual registration, just creates the resulting config file
            mock_powershelldsc_test = True

    if workspace_id is None or agent_id is None or oms_cert_path is None or oms_key_path is None \
            or endpoint is None or gpg_keyring_path is None or proxy_configuration_path is None \
            or working_directory is None or state_directory is None or vm_id is None:
        print "Missing mandatory arguments."
        print "Use -h or --help for usage."
        sys.exit(1)
    else:
        if mock_powershelldsc_test is True:
            # Don't validate paths if we want to generate a dummy config file
            pass
        else:
            # validate that the cert and key exists
            if os.path.isfile(oms_cert_path) is False or os.path.isfile(
                    oms_key_path) is False:
                raise Exception(
                    "Certificate or key file doesn't exist. Are you using absolute path?"
                )

        configuration.clear_config()
        configuration.set_config({
            configuration.PROXY_CONFIGURATION_PATH:
            proxy_configuration_path,
            configuration.WORKER_VERSION:
            "LinuxAutoRegister",
            configuration.WORKING_DIRECTORY_PATH:
            "/var/opt/microsoft/omsagent/tmp"
        })

        # build registration endpoint
        # example endpoint : agentsvc.azure-automation.net
        registration_endpoint = "https://" + workspace_id + "." + endpoint + "/accounts/" + workspace_id
        if "df-agentsvc" in registration_endpoint:
            registration_endpoint = "https://oaasagentsvcdf.test.azure-automation.net/accounts/" + workspace_id
            test_mode = True

        # rename to match oms concepts to automation
        machine_id = agent_id
        worker_group_name = get_hybrid_worker_group_name(agent_id=agent_id)

        # action
        if operation == REGISTER:
            if mock_powershelldsc_test is True:
                # Don't do the actual registration in case we want only a dummy registration file
                # create a dummy response instead
                registration_response = \
                    {'jobRuntimeDataServiceUri': 'https://we-jobruntimedata-prod-su1.azure-automation.net',
                     'AccountId': '23216587-8f56-428c-9006-4c2f28c036f5'}
                cert_info = [
                    '', '', '959GG850526XC5JT35E269CZ69A55E1C7E1256JH'
                ]
            else:
                # Update the metadata if possible
                platform_update_domain = ""
                tags = ""
                try:
                    http_client_factory = httpclientfactory.HttpClientFactory(
                        oms_cert_path, oms_key_path, test_mode)
                    http_client = http_client_factory.create_http_client(
                        sys.version_info)
                    metadata = get_metadata_from_imds(http_client)
                    if metadata is not None:
                        try:
                            vm_id = metadata["compute"]["vmId"]
                            sub_id = metadata["compute"]["subscriptionId"]
                            resource_group = metadata["compute"][
                                "resourceGroupName"]
                            vm_name = metadata["compute"]["name"]
                            azure_resource_id = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}".format(
                                sub_id, resource_group, vm_name)
                            platform_update_domain = metadata["compute"][
                                "platformUpdateDomain"]
                            tags = metadata["compute"]["tags"]

                        except KeyError:
                            pass
                except:
                    pass

                registration_response = register(registration_endpoint,
                                                 worker_group_name, machine_id,
                                                 oms_cert_path, oms_key_path,
                                                 is_azure_vm, vm_id,
                                                 azure_resource_id, test_mode,
                                                 platform_update_domain, tags)
                cert_info = linuxutil.get_cert_info(oms_cert_path)
                account_id = registration_response["AccountId"]

                if test_mode is False and diy_account_id is not None and diy_account_id != account_id:
                    sys.stderr.write(
                        "Unable to create worker configuration. DIY Automation account differs from "
                        "linked account.")
                    sys.exit(-5)

                create_worker_configuration_file(
                    working_directory,
                    registration_response["jobRuntimeDataServiceUri"],
                    registration_endpoint, workspace_id, account_id,
                    worker_group_name, machine_id, oms_cert_path, oms_key_path,
                    state_directory, gpg_keyring_path,
                    proxy_configuration_path, test_mode, cert_info,
                    is_azure_vm, vm_id)
        elif operation == DEREGISTER:
            deregister(registration_endpoint, worker_group_name, machine_id,
                       oms_cert_path, oms_key_path, test_mode)
        else:
            raise Exception(
                "No option specified, specify --register, --deregister or --help."
            )