Ejemplo n.º 1
0
def main():
    """Do the main thing here"""
    print("\n** Jamf category upload script")
    print("** Creates a category in Jamf Pro.")

    #  parse the command line arguments
    args = get_args()

    # grab values from a prefs file if supplied
    jamf_url, _, _, enc_creds = api_connect.get_creds_from_args(args)

    # now get the session token
    token = api_connect.get_uapi_token(jamf_url, enc_creds, args.verbose)

    # now process the list of categories
    for category_name in args.category:
        # check for existing category
        print("\nChecking '{}' on {}".format(category_name, jamf_url))
        obj_id = api_get.get_uapi_obj_id_from_name(jamf_url, "categories",
                                                   category_name, token,
                                                   args.verbose)
        if obj_id:
            print("Category '{}' already exists: ID {}".format(
                category_name, obj_id))
            upload_category(jamf_url, category_name, args.priority,
                            args.verbose, token, obj_id)
        else:
            # post the category
            upload_category(jamf_url, category_name, args.priority,
                            args.verbose, token)

    print()
Ejemplo n.º 2
0
def main():
    """Do the main thing here"""
    print("\n** Jamf Extension Attribute upload script")
    print("** Uploads Extension Attribute to Jamf Pro.")

    # parse the command line arguments
    args, cli_custom_keys = get_args()
    verbosity = args.verbose

    # grab values from a prefs file if supplied
    jamf_url, _, _, enc_creds = api_connect.get_creds_from_args(args)

    if not args.script:
        script = input("Enter the full path to the script to upload: ")
        args.script = script

    # now process the list of scripts
    for extatt_name in args.names:
        # check for existing Extension Attribute
        print("\nChecking '{}' on {}".format(extatt_name, jamf_url))
        obj_id = api_get.check_api_obj_id_from_name(jamf_url,
                                                    "extension_attribute",
                                                    extatt_name, enc_creds,
                                                    verbosity)
        if obj_id:
            print("Extension Attribute '{}' already exists: ID {}".format(
                extatt_name, obj_id))
            if args.replace:
                upload_extatt(
                    jamf_url,
                    enc_creds,
                    extatt_name,
                    args.script,
                    verbosity,
                    cli_custom_keys,
                    obj_id,
                )
            else:
                print(
                    "Not replacing existing Extension Attribute. Use --replace to enforce."
                )
        else:
            print("Extension Attribute '{}' not found - will create".format(
                extatt_name))
            upload_extatt(
                jamf_url,
                enc_creds,
                extatt_name,
                args.script,
                verbosity,
                cli_custom_keys,
            )

    print()
Ejemplo n.º 3
0
def main():
    """Do the main thing here"""
    print("\n** Jamf API Tool for Jamf Pro.\n")

    # parse the command line arguments
    args = get_args()
    verbosity = args.verbose

    # grab values from a prefs file if supplied
    jamf_url, _, _, slack_webhook, enc_creds = api_connect.get_creds_from_args(args)

    if args.slack:
        if not slack_webhook:
            print("slack_webhook value error. Please set it in your prefs file.")
            exit()

    # computers block ####
    if args.computer:
        if args.search and args.all:
            exit("syntax error: use either --search or --all, but not both")
        if not args.all:
            exit("syntax error: --computers requires --all as a minimum")

        recent_computers = []  # we'll need this later
        old_computers = []
        warning = []  # stores full detailed computer info
        compliant = []

        if args.all:
            """ fill up computers []"""
            obj = api_get.check_api_finds_all(
                jamf_url, "computer", enc_creds, verbosity
            )

            try:
                computers = []
                for x in obj:
                    computers.append(x["id"])

            except IndexError:
                computers = "404 computers not found"

            print(f"{len(computers)} computers found on {jamf_url}")

        for x in computers:
            """ load full computer info now """
            print(f"...loading info for computer {x}")
            obj = api_get.get_api_obj_value_from_id(
                jamf_url, "computer", x, "", enc_creds, verbosity
            )

            if obj:
                """ this is now computer object """
                try:
                    macos = obj["hardware"]["os_version"]
                    name = obj["general"]["name"]
                    dep = obj["general"]["management_status"]["enrolled_via_dep"]
                    seen = datetime.strptime(
                        obj["general"]["last_contact_time"], "%Y-%m-%d %H:%M:%S"
                    )
                    now = datetime.utcnow()

                except IndexError:
                    macos = "unknown"
                    name = "unknown"
                    dep = "unknown"
                    seen = "unknown"
                    now = "unknown"

                difference = (now - seen).days

            try:
                if (now - seen).days < 10 and not args.os:  # if recent
                    recent_computers.append(
                        f"{x} {macos}\t"
                        + f"name : {name}\n"
                        + f"\t\tDEP  : {dep}\n"
                        + f"\t\tseen : {difference} days ago"
                    )

                if (now - seen).days < 10 and args.os and (macos >= args.os):
                    compliant.append(
                        f"{x} {macos}\t"
                        + f"name : {name}\n"
                        + f"\t\tDEP  : {dep}\n"
                        + f"\t\tseen : {difference} days ago"
                    )
                elif (now - seen).days < 10 and args.os and (macos < args.os):
                    warning.append(
                        f"{x} {macos}\t"
                        + f"name : {name}\n"
                        + f"\t\tDEP  : {dep}\n"
                        + f"\t\tseen : {difference} days ago"
                    )

                if (now - seen).days > 10:
                    old_computers.append(
                        f"{x} {macos}\t"
                        + f"name : {name}\n"
                        + f"\t\tDEP  : {dep}\n"
                        + f"\t\tseen : {difference} days ago"
                    )

            except IndexError:
                print("checkin calc. error")

                # recent_computers.remove(f"{macos} {name} dep:{dep} seen:{calc}")

        # query is done
        print(bcolors.OKCYAN + "Loading complete...\n\nSummary:" + bcolors.ENDC)

        if args.os:
            """ summarise os """
            if compliant:
                print(f"{len(compliant)} compliant and recent:")
                for x in compliant:
                    print(bcolors.OKGREEN + x + bcolors.ENDC)
            if warning:
                print(f"{len(warning)} non-compliant:")
                for x in warning:
                    print(bcolors.WARNING + x + bcolors.ENDC)
            if old_computers:
                print(f"{len(old_computers)} stale - OS version not considered:")
                for x in old_computers:
                    print(bcolors.FAIL + x + bcolors.ENDC)
        else:
            """ regular summary """
            print(f"{len(recent_computers)} last check-in within the past 10 days")
            for x in recent_computers:
                print(bcolors.OKGREEN + x + bcolors.ENDC)
            print(f"{len(old_computers)} stale - last check-in more than 10 days")
            for x in old_computers:
                print(bcolors.FAIL + x + bcolors.ENDC)

        if args.slack:
            # end a slack api webhook with this number
            score = len(recent_computers) / (len(old_computers) + len(recent_computers))
            score = "{:.2%}".format(score)
            slack_payload = str(
                f":hospital: update health: {score} - {len(old_computers)} "
                f"need to be fixed on {jamf_url}\n"
            )
            print(slack_payload)

            data = {"text": slack_payload}
            for x in old_computers:
                print(bcolors.WARNING + x + bcolors.ENDC)
                if args.slack:  # werk
                    slack_payload += str(f"{x}\n")

            if args.slack:  # send to slack
                data = {"text": slack_payload}
                url = slack_webhook
                request_type = "POST"
                curl.request(request_type, url, enc_creds, verbosity, data)

        exit()

    # policy block #####
    if args.policies:
        if args.search and args.all:
            exit("syntax error: use --search or --all not both")
        if args.search:
            query = args.search

            obj = api_get.check_api_finds_all(jamf_url, "policy", enc_creds, verbosity)

            if obj:
                # targets is the new list
                targets = []
                print(
                    f"Searching {len(obj)} policy/ies on {jamf_url}:\n"
                    "To delete policies, obtain a matching query, then run with the "
                    "--delete argument"
                )

                for x in query:
                    for obj_item in obj:
                        # do the actual search
                        if x in obj_item["name"]:
                            targets.append(obj_item.copy())

                if len(targets) > 0:
                    print("Policies found:")
                    for target in targets:
                        print(
                            bcolors.WARNING
                            + f"- policy {target['id']}"
                            + f"\tname  : {target['name']}"
                            + bcolors.ENDC
                        )
                        if args.delete:
                            delete(target["id"], jamf_url, enc_creds, verbosity)
                    print("{} total matches".format(len(targets)))
                else:
                    for partial in query:
                        print("No match found: {}".format(partial))

        elif args.all:
            # assumes --policy flag due to prior exit on another style
            obj = api_get.check_api_finds_all(
                jamf_url, "category_all", enc_creds, verbosity
            )

            if obj:
                for x in obj:
                    # loop all the categories
                    print(
                        bcolors.OKCYAN
                        + f"category {x['id']}\t{x['name']}"
                        + bcolors.ENDC
                    )
                    obj = api_get.get_policies_in_category(
                        jamf_url, "category_all_items", x["id"], enc_creds, verbosity
                    )
                    if obj:
                        for x in obj:
                            # loop all the policies

                            # gather interesting info for each policy via API
                            # use a single call
                            # general/name
                            # scope/computer_groups  [0]['name']
                            generic_info = api_get.get_api_obj_value_from_id(
                                jamf_url, "policy", x["id"], "", enc_creds, verbosity
                            )

                            name = generic_info["general"]["name"]

                            try:
                                groups = generic_info["scope"]["computer_groups"][0][
                                    "name"
                                ]
                            except IndexError:
                                groups = "none"
                            try:
                                pkg = generic_info["package_configuration"]["packages"][
                                    0
                                ]["name"]
                            except IndexError:
                                pkg = "none"

                            # now show all the policies as each category loops
                            print(
                                bcolors.WARNING
                                + f"  policy {x['id']}"
                                + f"\tname  : {x['name']}\n"
                                + bcolors.ENDC
                                + f"\t\tpkg   : {pkg}\n"
                                + f"\t\tscope : {groups}"
                            )
            else:
                print("something went wrong: no categories found.")

            print(
                "\n"
                + bcolors.OKGREEN
                + "All policies listed above.. program complete for {}".format(jamf_url)
                + bcolors.ENDC
            )
            exit

        else:
            exit("syntax error: with --policies use --search or --all.")

    # set a list of names either from the CLI for Category erase all
    if args.category:
        categories = args.category
        print(
            "categories to check are:\n{}\nTotal: {}".format(
                categories, len(categories)
            )
        )
        # now process the list of categories
        for category_name in categories:
            category_name = category_name.replace(" ", "%20")
            # return all items found in each category
            print("\nChecking '{}' on {}".format(category_name, jamf_url))
            obj = api_get.get_policies_in_category(
                jamf_url, "category_all_items", category_name, enc_creds, verbosity
            )
            if obj:
                if not args.delete:

                    print(
                        f"Category '{category_name}' exists with {len(obj)} items: "
                        "To delete them run this command again with the --delete flag."
                    )

                for obj_item in obj:
                    print("~^~ {} -~- {}".format(obj_item["id"], obj_item["name"]))

                    if args.delete:
                        delete(obj_item["id"], jamf_url, enc_creds, verbosity)
            else:
                print("Category '{}' not found".format(category_name))

    # process a name or list of names
    if args.names:
        names = args.names
        print("policy names to check are:\n{}\nTotal: {}".format(names, len(names)))

        for policy_name in names:

            print("\nChecking '{}' on {}".format(policy_name, jamf_url))

            obj_id = api_get.get_api_obj_id_from_name(
                jamf_url, "policy", policy_name, enc_creds, verbosity
            )

            if obj_id:
                # gather info from interesting parts of the policy API
                # use a single call
                # general/name
                # scope/computer_gropus  [0]['name']
                generic_info = api_get.get_api_obj_value_from_id(
                    jamf_url, "policy", obj_id, "", enc_creds, verbosity
                )
                name = generic_info["general"]["name"]
                try:
                    groups = generic_info["scope"]["computer_groups"][0]["name"]
                except IndexError:
                    groups = ""

                print("Match found: '{}' ID: {} Group: {}".format(name, obj_id, groups))
                if args.delete:
                    delete(obj_id, jamf_url, enc_creds, verbosity)
            else:
                print("Policy '{}' not found".format(policy_name))

    print()
Ejemplo n.º 4
0
def main():
    """Do the main thing here"""
    print("\n** Jamf policy upload script")
    print("** Creates a policy in Jamf Pro.")

    # parse the command line arguments
    args, cli_custom_keys = get_args()
    verbosity = args.verbose

    # grab values from a prefs file if supplied
    jamf_url, _, _, enc_creds = api_connect.get_creds_from_args(args)

    # import computer group from file and replace any keys in the XML
    with open(args.template, "r") as file:
        template_contents = file.read()

    # substitute user-assignable keys
    template_contents = actions.substitute_assignable_keys(
        template_contents, cli_custom_keys, verbosity)

    #  set a list of names either from the CLI args or from the template if no arg provided
    if args.names:
        names = args.names
    else:
        names = [get_policy_name(template_contents, verbosity)]

    # now process the list of names
    for policy_name in names:
        # where a policy name was supplied via CLI arg, replace this in the template
        if args.names:
            template_contents = replace_policy_name(policy_name,
                                                    template_contents,
                                                    verbosity)

        # check for existing policy
        print("\nChecking '{}' on {}".format(policy_name, jamf_url))
        obj_id = api_get.check_api_obj_id_from_name(jamf_url, "policy",
                                                    policy_name, enc_creds,
                                                    verbosity)
        if obj_id:
            print("Policy '{}' already exists: ID {}".format(
                policy_name, obj_id))
            if args.replace:
                r = upload_policy(
                    jamf_url,
                    enc_creds,
                    policy_name,
                    template_contents,
                    cli_custom_keys,
                    verbosity,
                    obj_id,
                )
            else:
                print(
                    "Not replacing existing policy. Use --replace to enforce.")
        else:
            print("Policy '{}' not found - will create".format(policy_name))
            r = upload_policy(
                jamf_url,
                enc_creds,
                policy_name,
                template_contents,
                cli_custom_keys,
                verbosity,
            )

        # now upload the icon to the policy if specified in the args
        if args.icon:
            # get the policy_id returned from the HTTP response
            try:
                policy_id = ElementTree.fromstring(r.text).findtext("id")
                upload_policy_icon(
                    jamf_url,
                    enc_creds,
                    policy_name,
                    args.icon,
                    args.replace_icon,
                    verbosity,
                    policy_id,
                )
            except UnboundLocalError:
                upload_policy_icon(
                    jamf_url,
                    enc_creds,
                    policy_name,
                    args.icon,
                    args.replace_icon,
                    verbosity,
                )

    print()
def main():
    """Do the main thing here"""
    print(
        "\n** Jamf Configuration Profile upload script",
        "\n** Uploads Configuration Profile to Jamf Pro.",
        "\n** WARNING: This is an experimental script! Using it may have unexpected results!",
        "\n",
    )

    # parse the command line arguments
    args = get_args()
    verbosity = args.verbose

    # grab values from a prefs file if supplied
    jamf_url, jamf_user, jamf_pass, _, enc_creds = api_connect.get_creds_from_args(args)

    if verbosity > 1:
        print("URL: {}".format(jamf_url))
        print("User: {}".format(jamf_user))
    if verbosity > 2:
        print("Pass: {}".format(jamf_pass))

    # if an unsigned mobileconfig file is supplied we can get the name, organization and
    # description from it
    if args.mobileconfig:
        print("mobileconfig file supplied: {}".format(args.mobileconfig))
        # check if the file is signed
        mobileconfig_file = unsign_signed_mobileconfig(args.mobileconfig, verbosity)
        # quit if we get an unsigned profile back and we didn't select --unsign
        if mobileconfig_file and not args.unsign:
            print(
                "Signed profiles cannot be uploaded to Jamf Pro via the API. "
                "Use the GUI to upload the signed profile, or use --unsign to upload "
                "the profile with the signature removed."
            )
            exit()

        # import mobileconfig
        with open(mobileconfig_file, "rb") as file:
            mobileconfig_contents = plistlib.load(file)
        with open(mobileconfig_file, "rb") as file:
            mobileconfig_plist = file.read()
        try:
            mobileconfig_name = mobileconfig_contents["PayloadDisplayName"]
            print("Configuration Profile name: {}".format(mobileconfig_name))
            if verbosity > 2:
                print("\nMobileconfig contents:")
                print(mobileconfig_plist.decode("UTF-8"))
        except KeyError:
            exit("ERROR: Invalid mobileconfig file supplied - cannot import")
        try:
            description = mobileconfig_contents["PayloadDescription"]
        except KeyError:
            description = ""
        try:
            organization = mobileconfig_contents["PayloadOrganization"]
        except KeyError:
            organization = ""

    # otherwise we are dealing with a payload plist and we need a few other bits of info
    else:
        if not args.name:
            name = input("Enter the name of the configuration profile to upload: ")
            args.name = name
        if not args.payload:
            payload = input("Enter the full path to the payload plist to upload: ")
            args.payload = payload
        if not args.identifier:
            identifier = input("Enter the identifier of the custom payload to upload: ")
            args.identifier = identifier
        mobileconfig_name = args.name
        description = ""
        organization = ""

    # we provide a default template which has no category or scope
    if not args.template:
        # template = input("Enter the full path to the template XML to upload: ")
        args.template = "Jamf_Templates_and_Scripts/ProfileTemplate-default.xml"

    # automatically provide a description and organisation if not provided in the options
    if not args.description:
        if description:
            args.description = description
        else:
            description = input("Enter the description of the profile to upload: ")
            args.description = description
    if not args.organization:
        if organization:
            args.organization = organization
        else:
            organization = input("Enter the organization of the profile to upload: ")
            args.organization = organization

    # import profile template
    with open(args.template, "r") as file:
        template_contents = file.read()

    # check for existing Configuration Profile
    print("\nChecking '{}' on {}".format(mobileconfig_name, jamf_url))
    obj_id = api_get.get_api_obj_id_from_name(
        jamf_url, "os_x_configuration_profile", mobileconfig_name, enc_creds, verbosity
    )
    if obj_id:
        print(
            "Configuration Profile '{}' already exists: ID {}".format(
                mobileconfig_name, obj_id
            )
        )
        if args.replace:
            # grab existing UUID from profile as it MUST match on the destination
            existing_uuid = get_existing_uuid(jamf_url, obj_id, enc_creds, verbosity)

            if not args.mobileconfig:
                # generate the mobileconfig from the supplied payload
                mobileconfig_plist = make_mobileconfig_from_payload(
                    args.payload,
                    args.identifier,
                    mobileconfig_name,
                    args.organization,
                    args.description,
                    existing_uuid,
                    verbosity,
                )

            # now upload the mobileconfig by generating an XML template
            if mobileconfig_plist:
                upload_mobileconfig(
                    jamf_url,
                    enc_creds,
                    mobileconfig_name,
                    args.description,
                    args.category,
                    mobileconfig_plist,
                    args.computergroup_name,
                    template_contents,
                    existing_uuid,
                    verbosity,
                    obj_id,
                )
            else:
                print("A mobileconfig was not generated so cannot upload.")
        else:
            print(
                "Not replacing existing Configuration Profile. Use --replace to enforce."
            )
    else:
        print(
            "Configuration Profile '{}' not found - will create".format(
                mobileconfig_name
            )
        )
        new_uuid = generate_uuid()

        if not args.mobileconfig:
            # generate the mobileconfig from the supplied payload
            mobileconfig_plist = make_mobileconfig_from_payload(
                args.payload,
                args.identifier,
                mobileconfig_name,
                args.organization,
                args.description,
                new_uuid,
                verbosity,
            )

        # now upload the mobileconfig by generating an XML template
        if mobileconfig_plist:
            upload_mobileconfig(
                jamf_url,
                enc_creds,
                mobileconfig_name,
                args.description,
                args.category,
                mobileconfig_plist,
                args.computergroup_name,
                template_contents,
                new_uuid,
                verbosity,
            )
        else:
            print("A mobileconfig was not generated so cannot upload.")

    print()
def main():
    """Do the main thing here"""
    print("\n** Jamf computer group upload script")
    print("** Creates a computer group in Jamf Pro.")

    # parse the command line arguments
    args, cli_custom_keys = get_args()
    verbosity = args.verbose

    # grab values from a prefs file if supplied
    jamf_url, _, _, _, enc_creds = api_connect.get_creds_from_args(args)

    # import computer group from file and replace any keys in the XML
    with open(args.template, "r") as file:
        template_contents = file.read()

    # substitute user-assignable keys
    # pylint is incorrectly stating that 'verbosity' has no value. So...
    # pylint: disable=no-value-for-parameter
    template_contents = actions.substitute_assignable_keys(template_contents,
                                                           cli_custom_keys,
                                                           verbosity,
                                                           xml_escape=True)

    #  set a list of names either from the CLI args or from the template if no arg provided
    if args.names:
        names = args.names
    else:
        names = [get_computergroup_name(template_contents, verbosity)]

    # now process the list of names
    for computergroup_name in names:
        # where a group name was supplied via CLI arg, replace this in the template
        if args.names:
            template_contents = replace_computergroup_name(
                computergroup_name, template_contents, verbosity)

        # check for existing group
        print("\nChecking '{}' on {}".format(computergroup_name, jamf_url))
        obj_id = api_get.get_api_obj_id_from_name(jamf_url, "computer_group",
                                                  computergroup_name,
                                                  enc_creds, verbosity)
        if obj_id:
            print("Computer Group '{}' already exists: ID {}".format(
                computergroup_name, obj_id))
            if args.replace:
                upload_computergroup(
                    jamf_url,
                    enc_creds,
                    computergroup_name,
                    template_contents,
                    cli_custom_keys,
                    verbosity,
                    obj_id,
                )
            else:
                print(
                    "Not replacing existing Computer Group. Use --replace to enforce."
                )
        else:
            print("Computer Group '{}' not found - will create".format(
                computergroup_name))
            upload_computergroup(
                jamf_url,
                enc_creds,
                computergroup_name,
                template_contents,
                cli_custom_keys,
                verbosity,
            )

    print()
Ejemplo n.º 7
0
def main():
    """Do the main thing here"""
    print("\n** Jamf script upload script")
    print("** Uploads script to Jamf Pro.")

    #  parse the command line arguments
    args, cli_custom_keys = get_args()

    # grab values from a prefs file if supplied
    jamf_url, _, _, enc_creds = api_connect.get_creds_from_args(args)

    # now get the session token
    token = api_connect.get_uapi_token(jamf_url, enc_creds, args.verbose)

    if not args.script:
        script = input("Enter the full path to the script to upload: ")
        args.script = script

    # get the id for a category if supplied
    if args.category:
        print("Checking categories for {}".format(args.category))
        category_id = api_get.get_uapi_obj_id_from_name(
            jamf_url, "categories", args.category, token, args.verbose
        )
        if not category_id:
            print("WARNING: Category not found!")
            category_id = "-1"
        else:
            print("Category {} found: ID={}".format(args.category, category_id))
    else:
        args.category = ""

    # now process the list of scripts
    for script_path in args.script:
        script_name = os.path.basename(script_path)

        # check for existing script
        print("\nChecking '{}' on {}".format(script_name, jamf_url))
        if args.verbose:
            print("Full path: {}".format(script_path))
        obj_id = api_get.get_uapi_obj_id_from_name(
            jamf_url, "scripts", script_name, token, args.verbose
        )

        # post the script
        upload_script(
            jamf_url,
            script_name,
            script_path,
            category_id,
            args.category,
            args.info,
            args.notes,
            args.priority,
            args.parameter4,
            args.parameter5,
            args.parameter6,
            args.parameter7,
            args.parameter8,
            args.parameter9,
            args.parameter10,
            args.parameter11,
            args.osrequirements,
            args.verbose,
            token,
            cli_custom_keys,
            obj_id,
        )

    print()
Ejemplo n.º 8
0
def main():
    """Do the main thing here"""
    print("\n** Jamf package upload script")
    print("** Uploads packages to Jamf Cloud or SMB Distribution Points.")

    #  parse the command line arguments
    args = get_args()

    # grab values from a prefs file if supplied
    jamf_url, jamf_user, jamf_password, enc_creds = api_connect.get_creds_from_args(
        args)

    if args.prefs:
        smb_url, smb_user, smb_password = api_connect.get_smb_credentials(
            args.prefs)
    else:
        smb_url = ""
        smb_user = ""
        smb_password = ""

    # repeat for optional SMB share (but must supply a share path to invoke this)
    if args.share:
        smb_url = args.share
    if smb_url:
        if args.shareuser:
            smb_user = args.shareuser
        elif not smb_user:
            smb_user = input(
                "Enter a user with read/write permissions to {} : ".format(
                    smb_url))
        if args.sharepass:
            smb_password = args.sharepass
        elif not smb_password:
            smb_password = getpass.getpass(
                "Enter the password for '{}' : ".format(smb_user))

    # get HTTP request timeout
    r_timeout = float(args.timeout)

    if not args.pkg:
        pkg = input("Enter the full path to the package to upload: ")
        args.pkg = pkg

    # establish a web login session which is reusable for scraping tokens
    if args.direct:
        r, login_session = login(jamf_url, jamf_user, jamf_password,
                                 args.verbose)
        if r.status_code != 200:
            print("Failed to log in to the Jamf instance at: {}".format(
                jamf_url))

    # get the id for a category if supplied
    if args.category:
        print("Checking ID for {}".format(args.category))
        category_id = api_get.get_uapi_obj_id_from_name(
            jamf_url, enc_creds, args.category, args.verbose, "categories")
        if not category_id:
            print("WARNING: Category not found!")
            category_id = "-1"

    # now process the list of packages
    for pkg_path in args.pkg:
        pkg_name = os.path.basename(pkg_path)

        # See if the package is non-flat (requires zipping prior to upload).
        if os.path.isdir(pkg_path):
            pkg_path = zip_pkg_path(pkg_path)
            pkg_name += ".zip"

        # check for existing package
        print("\nChecking '{}' on {}".format(pkg_name, jamf_url))
        if args.verbose:
            print("Full path: {}".format(pkg_path))
        replace_pkg = True if args.replace else False
        obj_id = check_pkg(pkg_name, jamf_url, enc_creds)

        # post the package (won't run if the pkg exists and replace_pkg is False)
        # process for SMB shares if defined
        if smb_url:
            # mount the share
            mount_smb(smb_url, smb_user, smb_password, args.verbose)
            #  check for existing package
            local_pkg = check_local_pkg(args.share, pkg_name, args.verbose)
            if not local_pkg or replace_pkg:
                # copy the file
                copy_pkg(smb_url, pkg_path, pkg_name)
            # unmount the share
            umount_smb(smb_url)

        # otherwise process for cloud DP
        else:
            if obj_id == "-1" or replace_pkg:
                # JCDS direct upload method option
                if args.direct:
                    jcds_url, jcds_token, session_token = scrape_upload_token(
                        login_session, jamf_url, args.verbose)
                    if jcds_url and jcds_token and session_token:
                        if args.verbose:
                            print("JCDS URL: {}".format(jcds_url))
                            print("JCDS Upload token: {}".format(jcds_token))
                            print("Session token: {}".format(session_token))

                        #  post the package as chunks
                        post_pkg_chunks(
                            pkg_name,
                            pkg_path,
                            jcds_url,
                            jcds_token,
                            obj_id,
                            args.chunksize,
                            args.verbose,
                        )

                        #  now create the package object and get the pkg ID
                        pkg_id = update_pkg_by_form(
                            login_session,
                            session_token,
                            jamf_url,
                            pkg_name,
                            pkg_path,
                            obj_id,
                            category_id,
                            args.verbose,
                        )
                # curl -> dbfileupload upload method option
                elif args.curl:
                    r = curl_pkg(
                        pkg_name,
                        pkg_path,
                        jamf_url,
                        enc_creds,
                        obj_id,
                        r_timeout,
                        args.verbose,
                    )
                    try:
                        pkg_id = ElementTree.fromstring(r).findtext("id")
                        if pkg_id:
                            print("\nPackage uploaded successfully, ID={}".
                                  format(pkg_id))
                    except ElementTree.ParseError:
                        print("Could not parse XML. Raw output:")
                        print(r.decode("ascii"))
                    else:
                        if args.verbose:
                            if r:
                                print("\nResponse:\n")
                                print(r.decode("ascii"))
                            else:
                                print("No HTTP response")
                # requests -> dbfileupload upload method option
                else:
                    r = post_pkg(
                        pkg_name,
                        pkg_path,
                        jamf_url,
                        enc_creds,
                        obj_id,
                        r_timeout,
                        args.verbose,
                    )
                    # print result of the request
                    if r.status_code == 200 or r.status_code == 201:
                        pkg_id = ElementTree.fromstring(r.text).findtext("id")
                        print("\nPackage uploaded successfully, ID={}".format(
                            pkg_id))
                        if args.verbose:
                            print("HTTP POST Response Code: {}".format(
                                r.status_code))
                    else:
                        print("\nHTTP POST Response Code: {}".format(
                            r.status_code))
                    if args.verbose:
                        api_get.get_headers(r)

        # now process the package metadata if a category is supplied,
        # or if we are dealing with an SMB share
        if (args.category or smb_url) and not args.direct:
            try:
                pkg_id
                update_pkg_metadata(jamf_url, enc_creds, pkg_name,
                                    args.category, args.verbose, pkg_id)
            except UnboundLocalError:
                update_pkg_metadata(jamf_url, enc_creds, pkg_name,
                                    args.category, args.verbose)

    print()
Ejemplo n.º 9
0
def main():
    """Do the main thing here"""
    print("\n** Jamf package upload script")
    print("** Uploads packages to Jamf Cloud or SMB Distribution Points.")

    #  parse the command line arguments
    args = get_args()
    verbosity = args.verbose

    #  create a dictionary of package metadata from the args
    pkg_metadata = {
        "category": args.category,
        "info": args.info,
        "notes": args.notes,
        "reboot_required": args.reboot_required,
        "priority": args.priority,
        "os_requirement": args.os_requirement,
        "required_processor": args.required_processor,
        "send_notification": args.send_notification,
    }

    # grab values from a prefs file if supplied
    (
        jamf_url,
        jamf_user,
        jamf_password,
        slack_webhook,
        enc_creds,
    ) = api_connect.get_creds_from_args(args)

    if args.prefs:
        smb_url, smb_user, smb_pass = api_connect.get_smb_credentials(
            args.prefs)
    else:
        smb_url = ""
        smb_user = ""
        smb_pass = ""

    # repeat for optional SMB share (but must supply a share path to invoke this)
    if args.smb_url:
        smb_url = args.smb_url
        if args.smb_user:
            smb_user = args.smb_user
        if not smb_user:
            smb_user = input(
                "Enter a user with read/write permissions to {} : ".format(
                    smb_url))
        if args.smb_pass:
            smb_pass = args.smb_pass
        if not smb_pass:
            if not smb_pass:
                smb_pass = getpass.getpass(
                    "Enter the password for '{}' : ".format(smb_user))

    # get HTTP request timeout
    r_timeout = float(args.timeout)

    if not args.pkg:
        pkg = input("Enter the full path to the package to upload: ")
        args.pkg = pkg

    # establish a web login session which is reusable for scraping tokens
    if args.direct:
        r, login_session = login(jamf_url, jamf_user, jamf_password, verbosity)
        if r.status_code != 200:
            print("Failed to log in to the Jamf instance at: {}".format(
                jamf_url))

    # get the id for a category if supplied
    if args.category:
        print("Checking ID for category '{}'".format(args.category))

        # now get the session token
        token = api_connect.get_uapi_token(jamf_url, enc_creds, verbosity)

        category_id = api_get.get_uapi_obj_id_from_name(
            jamf_url, "categories", args.category, token, verbosity)
        if not category_id:
            print("WARNING: Category not found!")
            category_id = "-1"
        # add to the pkg_metadata dictionary
        pkg_metadata["category_id"] = category_id

    # now process the list of packages
    for pkg_path in args.pkg:
        pkg_name = os.path.basename(pkg_path)

        # See if the package is non-flat (requires zipping prior to upload).
        if os.path.isdir(pkg_path):
            pkg_path = zip_pkg_path(pkg_path)
            pkg_name += ".zip"

        #  calculate the SHA-512 hash of the package
        sha512string = sha512sum(pkg_path)

        # check for existing package
        print("\nChecking '{}' on {}".format(pkg_name, jamf_url))
        if verbosity:
            print("Full path: {}".format(pkg_path))
        replace_pkg = True if args.replace else False
        obj_id = check_pkg(pkg_name, jamf_url, enc_creds, verbosity)
        if obj_id != "-1":
            print("Package '{}' already exists: ID {}".format(
                pkg_name, obj_id))
            pkg_id = obj_id  # assign pkg_id for smb runs - JCDS runs get it from the pkg upload
        else:
            pkg_id = ""

        # post the package (won't run if the pkg exists and replace_pkg is False)
        # process for SMB shares if defined
        if args.smb_url:
            # mount the share
            smb_actions.mount_smb(args.smb_url, args.smb_user, args.smb_pass,
                                  verbosity)
            # check for existing package
            local_pkg = check_local_pkg(args.smb_url, pkg_name, verbosity)
            if not local_pkg or replace_pkg:
                # copy the file
                smb_actions.copy_pkg(args.smb_url, pkg_path, pkg_name)
            # unmount the share
            smb_actions.umount_smb(args.smb_url)

        # otherwise process for cloud DP
        else:
            if obj_id == "-1" or replace_pkg:
                # JCDS direct upload method option
                if args.direct:
                    jcds_url, jcds_token, session_token = scrape_upload_token(
                        login_session, jamf_url, verbosity)
                    if jcds_url and jcds_token and session_token:
                        if verbosity:
                            print("JCDS URL: {}".format(jcds_url))
                            print("JCDS Upload token: {}".format(jcds_token))
                            print("Session token: {}".format(session_token))

                        #  post the package as chunks
                        post_pkg_chunks(
                            pkg_name,
                            pkg_path,
                            jcds_url,
                            jcds_token,
                            obj_id,
                            args.chunksize,
                            verbosity,
                        )

                        #  now create the package object and get the pkg ID
                        pkg_id = update_pkg_by_form(
                            login_session,
                            session_token,
                            jamf_url,
                            pkg_name,
                            pkg_path,
                            obj_id,
                            pkg_metadata,
                            verbosity,
                        )
                # curl -> dbfileupload upload method option
                elif args.nscurl:
                    r = nscurl_pkg(
                        pkg_name,
                        pkg_path,
                        jamf_url,
                        enc_creds,
                        obj_id,
                        r_timeout,
                        verbosity,
                    )
                    try:
                        pkg_id = ElementTree.fromstring(r).findtext("id")
                        if pkg_id:
                            print("\nPackage uploaded successfully, ID={}".
                                  format(pkg_id))
                    except ElementTree.ParseError:
                        print("Could not parse XML. Raw output:")
                        print(r.decode("ascii"))
                    else:
                        if verbosity:
                            if r:
                                print("\nResponse:\n")
                                print(r.decode("ascii"))
                            else:
                                print("No HTTP response")
                # requests -> dbfileupload upload method option
                elif args.requests:
                    r = post_pkg(
                        pkg_name,
                        pkg_path,
                        jamf_url,
                        enc_creds,
                        obj_id,
                        r_timeout,
                        verbosity,
                    )
                    # print result of the request
                    if r.status_code == 200 or r.status_code == 201:
                        pkg_id = ElementTree.fromstring(r.text).findtext("id")
                        print("\nPackage uploaded successfully, ID={}".format(
                            pkg_id))
                        if verbosity:
                            print("HTTP POST Response Code: {}".format(
                                r.status_code))
                    else:
                        print("\nHTTP POST Response Code: {}".format(
                            r.status_code))
                    if verbosity:
                        api_get.get_headers(r)
                # curl -> dbfileupload upload method option
                else:
                    r = curl_pkg(
                        pkg_name,
                        pkg_path,
                        jamf_url,
                        enc_creds,
                        obj_id,
                        r_timeout,
                        verbosity,
                    )
                    try:
                        pkg_id = ElementTree.fromstring(r).findtext("id")
                        if pkg_id:
                            print("\nPackage uploaded successfully, ID={}".
                                  format(pkg_id))
                    except ElementTree.ParseError:
                        print("Could not parse XML. Raw output:")
                        print(r.decode("ascii"))
                    else:
                        if verbosity:
                            if r:
                                print("\nResponse:\n")
                                print(r.decode("ascii"))
                            else:
                                print("No HTTP response")

        # now process the package metadata if a category is supplied,
        # or if we are dealing with an SMB share
        if not args.direct:
            if pkg_id:
                if verbosity:
                    print("Updating package metadata for {}".format(pkg_id))
                update_pkg_metadata(
                    jamf_url,
                    enc_creds,
                    pkg_name,
                    pkg_metadata,
                    sha512string,
                    verbosity,
                    pkg_id,
                )
            else:
                if verbosity:
                    print("Creating package metadata")
                update_pkg_metadata(jamf_url, enc_creds, pkg_name,
                                    pkg_metadata, sha512string, verbosity)

    print()