スケジュール済ジョブおよび作業リクエストの管理

OS管理のスケジュール済ジョブおよび作業リクエストを管理する方法について説明します。

スケジュール済ジョブ

OS管理サービスを使用して管理対象インスタンスまたは管理対象インスタンス・グループの更新を管理する場合、アクションが実行されるタイミングを完全に制御できます。

アクションがすぐに実行されるように指定すると、OS管理サービスによって作業リクエストが作成されます。

特定の日時にアクションが実行されるように指定すると、OS管理サービスによってスケジュール済ジョブが作成されます。スケジュール済ジョブには、次の2つの基本モードがあります:

  • ジョブが1回実行されるスケジュール済ジョブ。

    更新または更新セットのインストールなどのタスクには、1回かぎりのジョブをスケジュールできます。これらのタスクは、1回かぎりのイベントに固有のアクティビティを表します。たとえば、アプリケーションをサポートするPythonなどの特定のパッケージ・バージョンをインストールする1回かぎりのジョブをスケジュールできます。これらのアクションをスケジュールする場合、アクションをすぐに実行するか、「カスタム・スケジュール」を選択して、1回かぎりのジョブをスケジュールする日時を選択できます。

  • 指定した間隔でジョブが繰り返されるスケジュール済ジョブ。

    管理対象インスタンス・グループに使用可能なすべての更新のインストールなどのタスクには、繰返しジョブをスケジュールできます。たとえば、毎週、特定の時刻にすべてのセキュリティ更新をインストールするようにジョブをスケジュールできます。これらのアクションをスケジュールする場合、アクションをすぐに実行するか、「カスタム・スケジュール」を選択できます。カスタム・スケジュールの場合、ジョブを最初に実行する日時を選択し、オプションで、指定した間隔(毎時毎日毎週または毎月)でジョブを繰り返すように設定できます。

スケジュールした日時に達すると、アクションを実行するための作業リクエストが1つ以上作成されます。ジョブをすぐに実行したり、ジョブを削除したり、繰返しジョブをスキップするなど、スケジュール済ジョブを完全に制御できます。OS管理サービスでは、スケジュール済ジョブおよび関連する作業リクエストの完全な履歴が保持されます。

ノート

スケジュール済ジョブをサポートするタスクの詳細は、Linuxパッケージの管理およびWindows更新の管理を参照してください。

作業リクエスト

更新のインストールや削除などのアクションは非同期であり、作業リクエストが開始されます。アクションが失敗した理由を確認できるなど、作業リクエストを使用してこれらの操作のステータスをトラッキングできます。OS管理サービスでは、管理対象インスタンスまたは管理対象インスタンス・グループの作業リクエストの完全な履歴が保持されます。

作業リクエストの状態

作業リクエストの状態は次のとおりです:

受入れ済
リクエストは処理対象の作業リクエスト・キューにあります。
進行中
作業リクエストは処理中です。
成功
作業リクエストは正常に処理されました。
失敗
作業リクエストは正常に処理されませんでした。作業リクエスト・ログを確認して問題を特定し、トラブルシューティングを実行できます。
取消中
作業リクエストが取り消されています。
取消済
作業リクエストは取り消されました。
ノート

OS管理サービスは、正常に完了したか失敗した2週間より古い作業リクエストをクリーンアップします。開始されていない、または進行中の作業リクエストはクリーンアップされません。

コンソールの使用

スケジュール済ジョブを管理するには
  1. ナビゲーション・メニューを開き、「コンピュート」をクリックします。「OS管理」「スケジュール済ジョブ」をクリックします。
  2. 「リスト・スコープ」セクションで、スケジュール済ジョブを含むコンパートメントを選択します。
  3. スケジュール済ジョブの横で、「アクション」アイコン(3つのドット)をクリックしてアクションを選択します:
    • 詳細の表示: ジョブの影響を受けるインスタンスおよび実行するアクションを確認します。
    • 即時実行: スケジュールをオーバーライドし、ジョブをすぐに実行します。
    • スキップ: (繰返しスケジュール済ジョブのみ)スケジュール済ジョブを次回のスケジュール済ジョブまで遅延させます。
    • 削除: スケジュール済ジョブを取り消します。

APIの使用

APIの使用およびリクエストの署名の詳細は、REST APIおよびセキュリティ資格証明を参照してください。SDKの詳細は、ソフトウェア開発キットおよびコマンドライン・インタフェースを参照してください。

スケジュール済ジョブ

スケジュール済ジョブの作業には、次のAPI操作を使用します:

作業リクエスト

作業リクエストの分析には、次のAPI操作を使用します:

OS管理サービスで使用可能なAPI操作の完全なリストは、OS管理APIを参照してください。

Python SDKを使用したコンプライアンス・レポートの生成

この項では、OS管理APIを利用するPythonスクリプトの例(compliance_report.py)を使用してセキュリティ・コンプライアンス・レポートを実行する方法を示します。Pythonスクリプトの例では、セキュリティ更新が欠落しているすべての管理対象インスタンスについて、テナンシ全体またはコンパートメントごとのセキュリティ・コンプライアンス・レポートを生成します。

ノート

Python SDKを使用して、Oracle Cloud Infrastructureのリソースを管理するためのコードを記述できます。詳細は、Python SDKを参照してください。
ヒント

セキュリティ・コンプライアンス・レポートの実行方法を示すビデオ・デモンストレーションについては、ビデオ: Linuxインスタンスのコンプライアンス・レポートの作成を参照してください。
compliance_report.py

#!/usr/bin/env python3

#
# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at
#  http://oss.oracle.com/licenses/upl
#

import os
import sys
import oci
import time
import logging
import logging.handlers

from argparse import ArgumentParser, ArgumentError, Namespace, SUPPRESS
from oci.os_management import OsManagementClient
from oci.identity import IdentityClient

PROGRAM_NAME = os.path.basename(sys.argv[0]).replace('.py', '')
PROGRAM_VERSION = '0.1.0'


def setup_logger(enable_logfile=False, verbose=False, debug=False):
    class LevelsFilter(logging.Filter):
        def __init__(self, levels, name=''):
            logging.Filter.__init__(self, name)
            self.levels = levels

        def filter(self, record):
            if record.levelno in self.levels:
                return True
            return False

    flat_formatter = logging.Formatter('{message}', style='{')
    level_formatter = logging.Formatter('{levelname:8s}: {message}', style='{')
    log_file_handler = None
    if enable_logfile or debug:
        try:
            formatter = logging.Formatter('{asctime} - {name} - {levelname}({module}:{lineno}) - {message}', style='{')
            log_file_handler = logging.handlers.RotatingFileHandler('{0}.log'.format(PROGRAM_NAME),
                                                                    mode='a',
                                                                    maxBytes=1024 * 1024,
                                                                    backupCount=3)
            log_file_handler.setFormatter(formatter)
            log_file_handler.setLevel(logging.NOTSET)
        except IOError:
            pass

    logger = logging.getLogger(PROGRAM_NAME)
    stdout_handler = logging.StreamHandler(stream=sys.stdout)
    stdout_handler.setFormatter(flat_formatter)
    logger.setLevel(logging.ERROR)
    if verbose:
        stdout_handler.addFilter(LevelsFilter([logging.INFO]))
        logger.setLevel(logging.INFO)
    if debug:
        log_file_handler.setFormatter(level_formatter)
        log_file_handler.addFilter(LevelsFilter([logging.DEBUG,
                                                 logging.INFO,
                                                 logging.WARNING,
                                                 logging.ERROR,
                                                 logging.CRITICAL]))
        logger.setLevel(logging.DEBUG)
    stderr_handler = logging.StreamHandler(stream=sys.stderr)
    stderr_handler.setFormatter(level_formatter)
    stderr_handler.addFilter(LevelsFilter([logging.WARNING, logging.ERROR, logging.CRITICAL]))
    if log_file_handler is not None:
        logger.addHandler(log_file_handler)
    logger.addHandler(stdout_handler)
    logger.addHandler(stderr_handler)
    return logger


class OCIClients(object):
    def __init__(self, options):
        self.compartment_id = options.compartment_id
        if options.use_instance_principles:
            config = {}
            if options.region is not None:
                config['region'] = options.region
            signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
            if self.compartment_id is None:
                self.compartment_id = signer.tenancy_id
            self.osms_client = OsManagementClient(config,
                                                  timeout=(10, 600),
                                                  retry_strategy=oci.retry.DEFAULT_RETRY_STRATEGY,
                                                  signer=signer)
            self.iam_client = IdentityClient(config,
                                             timeout=(10, 600),
                                             retry_strategy=oci.retry.DEFAULT_RETRY_STRATEGY,
                                             signer=signer)
        else:
            config = oci.config.from_file(file_location=options.config_file, profile_name=options.config_profile)
            if options.region is not None:
                config['region'] = options.region
            if self.compartment_id is None:
                self.compartment_id = config.get('tenancy')
            else:
                config['compartment'] = self.compartment_id
            self.osms_client = OsManagementClient(config,
                                                  timeout=(10, 600),
                                                  retry_strategy=oci.retry.DEFAULT_RETRY_STRATEGY)
            self.iam_client = IdentityClient(config,
                                             timeout=(10, 600),
                                             retry_strategy=oci.retry.DEFAULT_RETRY_STRATEGY)

    def get_osms_client(self):
        return self.osms_client

    def get_iam_client(self):
        return self.iam_client


class Data(object):
    def __init__(self):
        self.managed_instance_group_id = None
        self.total_instances = 0
        self.vulnerable_instances = []

    def show_overview(self):
        print()
        print('        Patch Compliance Report Sample')
        print('        ==============================')
        print()
        if self.managed_instance_group_id is not None:
            print('Managed Instance Group ID: {0}'.format(self.managed_instance_group_id))
        vulnerable_instances = len(self.vulnerable_instances) if len(self.vulnerable_instances) > 0 else 'None'
        linking_verb = 'are' if len(self.vulnerable_instances) > 1 else 'is'
        plural = 's' if self.total_instances > 1 else ''
        print('\nDetected out of {0} managed instance{1}, {2} {3} '
              'missing security patches!\n'.format(self.total_instances, plural, vulnerable_instances, linking_verb))

    def show_details(self):
        for instance in self.vulnerable_instances:
            print('Managed Instance {0} ({1})'.format(instance.get('display_name'), instance.get('id')))
            print(' has the following outstanding security patches:')
            for update in instance.get('security_updates', []):
                print('  {0}'.format(update.get('display_name')))
                if update.get('related_cves', None) is not None:
                    print('      CVEs:', end='')
                    num_cve = 0
                    num_cves = len(update.get('related_cves', []))
                    cve_line = ''
                    for cve in update.get('related_cves', []):
                        num_cve += 1
                        cve_line = '{0} {1:<17}'.format(cve_line, cve + ',')
                        if len(cve_line) >= 65:
                            if num_cve < num_cves:
                                print(cve_line.rstrip(' '))
                                print('           ', end='')
                            else:
                                print(cve_line.rstrip(', '))
                            cve_line = ''
                    if cve_line != '':
                        print(cve_line.rstrip(', '))
            print()


def find_all_compartments(iam_client, compartment_id):
    LOGGER.info('Find sub compartments')
    compartment_ids = [compartment_id]
    if compartment_id.startswith('ocid1.tenancy.'):
        c_response = iam_client.list_compartments(compartment_id, compartment_id_in_subtree=True)
        compartment_ids.extend([c.id for c in c_response.data
                                if c.lifecycle_state == 'ACTIVE' and c.name != 'ManagedCompartmentForPaaS'])
    else:
        compartment_ids.extend(list_compartments(iam_client, compartment_ids))
    return compartment_ids


def list_compartments(iam_client, compartments):
    if compartments is None:
        return []
    sub_compartments = []
    for compartment in compartments:
        LOGGER.debug('List Compartment: {0}'.format(compartment))
        c_response = iam_client.list_compartments(compartment)
        sub_compartments.extend([c.id for c in c_response.data
                                 if c.lifecycle_state == 'ACTIVE' and c.name != 'ManagedCompartmentForPaaS'])
        sub_names = [c.name for c in c_response.data
                     if c.lifecycle_state == 'ACTIVE' and c.name != 'ManagedCompartmentForPaaS']
        LOGGER.debug('Sub Compartments: {0}'.format(sub_names))
        sub_compartments.extend(list_compartments(iam_client, sub_compartments))
    return sub_compartments


def query_managed_instance_group(osms_client, managed_instance_group_id):
    LOGGER.info('Retrieving Managed Instance Group ({0}) info'.format(managed_instance_group_id))
    mig_response = osms_client.get_managed_instance_group(managed_instance_group_id)
    total_instances, vuln_instances = query_managed_instances(osms_client, mig_response.data.managed_instances)
    return total_instances, vuln_instances


def query_compartment(osms_client, compartment_id):
    LOGGER.info('Retrieving Managed Instance list from compartment "{0}"'.format(compartment_id))
    request_options = {'limit':      10,
                       'sort_by':    'TIMECREATED',
                       'sort_order': 'ASC',
                       }
    managed_instances = []
    next_page = None
    has_next_page = True
    while has_next_page:
        if next_page is not None:
            request_options['page'] = next_page
        elif request_options.get('page', False):
            request_options.pop('page')
        try:
            mil_response = osms_client.list_managed_instances(compartment_id)  # , **request_options)
        except oci.exceptions.ServiceError as service_error:
            LOGGER.info('Service Exception "{0}")'.format(service_error.code))
            LOGGER.debug('OCI Request ID: "{0}"'.format(service_error.request_id))
            raise
        if mil_response is None:
            raise RuntimeError('Unable to retrieve updates from compartment {0}'.format(compartment_id))
        has_next_page = mil_response.has_next_page
        next_page = mil_response.next_page
        managed_instances.extend(mil_response.data)
    total_instances, vuln_instances = query_managed_instances(osms_client, managed_instances)
    return total_instances, vuln_instances


def query_managed_instances(osms_client, managed_instances):
    vuln_instances = []
    total_instances = len(managed_instances)
    for mi in managed_instances:
        LOGGER.info('Retrieving Managed Instance "{0}" info'.format(mi.display_name))
        mi_response = osms_client.get_managed_instance(mi.id)
        mi_data = mi_response.data
        LOGGER.debug('Managed Instance: {0}'.format(mi_data))
        linux_mi = mi_data.os_family == 'LINUX'
        available_security_updates = 0
        security_updates = []
        updates = None
        if mi_data.updates_available > 0:
            LOGGER.info('Retrieving Update info for Managed Instance "{0}"'.format(mi.display_name))
            request_options = {'limit':      10,
                               'sort_by':    'TIMECREATED',
                               'sort_order': 'ASC',
                               }
            next_page = None
            has_next_page = True
            while has_next_page:
                if next_page is not None:
                    request_options['page'] = next_page
                elif request_options.get('page', False):
                    request_options.pop('page')
                try:
                    if linux_mi:
                        updates = osms_client.list_available_updates_for_managed_instance(mi.id, **request_options)
                    else:
                        updates = osms_client.list_available_windows_updates_for_managed_instance(mi.id,
                                                                                                  **request_options)
                except oci.exceptions.ServiceError as service_error:
                    LOGGER.info('Service Exception "{0}")'.format(service_error.code))
                    LOGGER.debug('OCI Request ID: "{0}"'.format(service_error.request_id))
                if updates is None:
                    raise RuntimeError('Unable to retrieve updates from {0}'.format(mi.display_name))
                has_next_page = updates.has_next_page
                next_page = updates.next_page
                for update in updates.data:
                    LOGGER.debug('Update: {0}'.format(update))
                    if update.update_type == 'SECURITY':
                        available_security_updates += 1
                        if linux_mi:
                            security_updates.append({'display_name': update.display_name,
                                                     'related_cves': update.related_cves})
                        else:
                            security_updates.append({'display_name': update.display_name})
            if available_security_updates > 0:
                managed_instance = {
                        'compartment_id':             mi_data.compartment_id,
                        'display_name':               mi_data.display_name,
                        'id':                         mi_data.id,
                        'is_reboot_required':         mi_data.is_reboot_required,
                        'last_boot':                  mi_data.last_boot,
                        'last_checkin':               mi_data.last_checkin,
                        'managed_instance_groups':    mi_data.managed_instance_groups,
                        'os_family':                  mi_data.os_family,
                        'os_kernel_version':          mi_data.os_kernel_version,
                        'os_name':                    mi_data.os_name,
                        'os_version':                 mi_data.os_version,
                        'status':                     mi_data.status,
                        'updates_available':          mi_data.updates_available,
                        'security_updates_available': available_security_updates,
                        'security_updates':           security_updates
                }
                vuln_instances.append(managed_instance)
    return total_instances, vuln_instances


def main(argv=None):
    if argv is None:
        argv = sys.argv
    program_version_message = '{0} {1}'.format(PROGRAM_NAME, PROGRAM_VERSION)
    program_description = 'OS Management Reporting'
    options = Namespace()
    try:
        the_parser = ArgumentParser(description=program_description)
        select_grp = the_parser.add_mutually_exclusive_group()
        authop_grp = the_parser.add_mutually_exclusive_group()
        the_parser.add_argument('--compartment-ocid', '-c',
                                dest='compartment_id',
                                action='store',
                                default=None,
                                required=True,
                                help="Compartment to run query against, defaults to the root compartment")
        the_parser.add_argument('--region', '-r',
                                dest='region',
                                action='store',
                                default=None,
                                required=False,
                                help="Region to run query against")
        authop_grp.add_argument('--use-instance-principles', '-I',
                                dest='use_instance_principles',
                                action='store_true',
                                default=False,
                                required=False,
                                help="Authenticate using Instance Principles")
        select_grp.add_argument('--managed-instance-group-ocid', '-g',
                                dest='managed_instance_group_id',
                                action='store',
                                default=None,
                                required=False,
                                help="Managed Instance Group to query")
        authop_grp.add_argument('--oci-config', '-C',
                                dest='config_file',
                                action='store',
                                default=os.path.join('~', '.oci', 'config'),
                                required=False,
                                help="Oracle Cloud Infrastructure config file")
        the_parser.add_argument('--oci-config-profile', '-P',
                                dest='config_profile',
                                action='store',
                                default='DEFAULT',
                                required=False,
                                help="Oracle Cloud Infrastructure config profile to use")
        the_parser.add_argument('--show-details', '-d',
                                dest='show_details',
                                action='store_true',
                                default=False,
                                required=False,
                                help='Show detailed report')
        select_grp.add_argument('--recursive', '-R',
                                dest='scan_sub_compartments',
                                action='store_true',
                                default=False,
                                required=False,
                                help='Recursively scan sub-compartments')
        the_parser.add_argument('--verbose', '-v',
                                dest='verbose',
                                action='store_true',
                                default=False,
                                required=False,
                                help='Enable verbose mode')
        the_parser.add_argument("--version",
                                action="version",
                                version=program_version_message)
        the_parser.add_argument("--debug",
                                dest='debug_enabled',
                                action='store_true',
                                default=False,
                                required=False,
                                help=SUPPRESS)
        the_parser.parse_args(args=argv[1:], namespace=options)
    except ArgumentError:
        return 1
    # noinspection PyGlobalUndefined
    global LOGGER
    LOGGER = setup_logger(verbose=options.verbose, debug=options.debug_enabled)
    oci_clients = OCIClients(options)
    data = Data()
    if options.managed_instance_group_id is not None:
        total_instances, vulnerable_instances = query_managed_instance_group(oci_clients.osms_client,
                                                                             options.managed_instance_group_id)
        data.total_instances += total_instances
        data.vulnerable_instances.extend(vulnerable_instances)
        data.managed_instance_group_id = options.managed_instance_group_id
        LOGGER.debug('MIG Query: Total Instances {0}, Vulnerable Instances {1}'.format(total_instances,
                                                                                       vulnerable_instances))
    else:
        compartment_ids = [options.compartment_id]
        if options.scan_sub_compartments:
            compartment_ids = find_all_compartments(oci_clients.iam_client, options.compartment_id)
        for compartment_id in compartment_ids:
            total_instances, vulnerable_instances = query_compartment(oci_clients.osms_client, compartment_id)
            data.total_instances += total_instances
            data.vulnerable_instances.extend(vulnerable_instances)
            LOGGER.debug('Compartment ({0}) Instances: Total {1}, Vulnerable {2}'.format(compartment_id,
                                                                                         total_instances,
                                                                                         len(vulnerable_instances)))
    data.show_overview()
    if options.show_details:
        data.show_details()
    return 0


if __name__ == '__main__':
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        print('\n{0}: Cancelled by user'.format(PROGRAM_NAME))
        sys.exit(127)
compliance_report.pyスクリプトを使用してセキュリティ・コンプライアンス・レポートを実行するには

  1. Pythonがインストールされていることを確認してください。

    たとえば、Oracle Linuxで次のコマンドを実行します:

    sudo yum install python3 -y
  2. viなどのファイル・エディタで、この例で使用するサンプルPythonスクリプトcompliance_report.pyを作成します。

    例:

    sudo vi compliance_report.py
  3. compliance_report.pyの内容をファイルにコピーします。
  4. スクリプトを実行可能にします。

    例:

    chmod +x compliance_report.py
  5. スクリプトを実行してセキュリティ・コンプライアンス・レポートを生成します。

    スクリプトの実行に使用可能なオプションを表示するには、--helpオプションを指定してcompliance_report.pyスクリプトを実行します。

    # python3 compliance_report.py --help
    usage: compliance_report.py [-h] --compartment-ocid COMPARTMENT_ID
                                [--region REGION] [--use-instance-principles]
                                [--managed-instance-group-ocid MANAGED_INSTANCE_GROUP_ID]
                                [--oci-config CONFIG_FILE]
                                [--oci-config-profile CONFIG_PROFILE]
                                [--show-details] [--recursive] [--verbose]
                                [--version]
    
    OS Management Reporting
    
    optional arguments:
      -h, --help            show this help message and exit
      --compartment-ocid COMPARTMENT_ID, -c COMPARTMENT_ID
                            Compartment to run query against, defaults to the root
                            compartment
      --region REGION, -r REGION
                            Region to run query against
      --use-instance-principles, -I
                            Authenticate using Instance Principles
      --managed-instance-group-ocid MANAGED_INSTANCE_GROUP_ID, -g MANAGED_INSTANCE_GROUP_ID
                            Managed Instance Group to query
      --oci-config CONFIG_FILE, -C CONFIG_FILE
                            Oracle Cloud Infrastructure config file
      --oci-config-profile CONFIG_PROFILE, -P CONFIG_PROFILE
                            Oracle Cloud Infrastructure config profile to use
      --show-details, -d    Show detailed report
      --recursive, -R       Recursively scan sub-compartments
      --verbose, -v         Enable verbose mode
      --version             show program's version number and exit
    重要

    compliance_report.pyスクリプトを使用する場合は、次の使用上のガイドラインに留意してください:

    • このサンプル・スクリプトを正常に実行するには、コンパートメントの読取りおよび問合せのための適切な権限が必要です。
    • --compartment-ocidオプションは必須です。Compartment_IDには、テナンシOCID (ルート・コンパートメント)またはテナンシ内のコンパートメントのOCIDを指定します。
    • --use-instance-principlesオプションは、Oracle Cloud Infrastructureインスタンスの原則を使用して認証するようにスクリプトを設定します。このオプションを使用しない場合は、~/.oci/configにプロファイルを設定する必要があります。
    • --recursiveオプションは、そのリージョン内のコンパートメントおよびサブコンパートメントのレポートを生成します。--recursiveオプションは、管理対象インスタンス・グループとともに使用できません。コンパートメント・レベルでレポートを実行する場合は、--recursiveオプションを使用しないでください。
    • --show detailsオプションを使用すると、管理対象インスタンスごとに欠落している特定のセキュリティ・パッチの詳細なリストが出力に表示されます。このオプションを指定せずにスクリプトを実行すると、出力では、更新が欠落している管理対象インスタンスと欠落している更新の数のみがハイライトされます。
    • --regionオプションは省略可能です。
      • ~/.oci/configに特定のリージョンを指定した場合、--regionオプションを使用して、このパラメータに指定したリージョンでこのリージョンをオーバーライドできます。
      • インスタンス原則を使用している場合は、あるリージョンでスクリプトを実行し、--regionオプションを使用して別のリージョンをターゲット指定できます。

    次の例では、compliance_report.pyスクリプトを使用してサンプル・レポートを生成する方法を示します:

    # python3 compliance_report.py --use-instance-principles --compartment-ocid=ocid1.tenancy.oc1..<unique_ID> --recursive --show-details --region=eu-zurich-1
    
            Patch Compliance Report Sample
            ==============================
    
    
    Detected out of 7 managed instances, 7 are missing security patches!
    
    Managed Instance sqa-test-prod-zurich-win2019srvstd (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      2020-04 Cumulative Update for Windows Server 2019 (1809) for x64-based Systems (KB4549949)
    
    Managed Instance sqa-test-prod-zurich-win2016srvstd (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      Security Intelligence Update for Windows Defender Antivirus - KB2267602 (Version 1.315.54.0)
      2020-04 Servicing Stack Update for Windows Server 2016 for x64-based Systems (KB4550994)
    
    Managed Instance sqa-test-prod-zurich-win2012srvstd (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      2020-04 Security Monthly Quality Rollup for Windows Server 2012 R2 for x64-based Systems (KB4550961)
    
    Managed Instance sqatest-ol6-0 (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      libcurl
          CVEs: CVE-2019-5482
      python
          CVEs: CVE-2018-20852
      kernel-uek-firmware
          CVEs: CVE-2018-5953,    CVE-2019-18806,   CVE-2020-10942
      python-libs
          CVEs: CVE-2018-20852
      sudo
          CVEs: CVE-2019-18634
      curl
          CVEs: CVE-2019-5482
      kernel-uek
          CVEs: CVE-2018-5953,    CVE-2019-18806,   CVE-2020-10942
      kernel
          CVEs: CVE-2017-1000371, CVE-2019-17666
      libicu
          CVEs: CVE-2020-10531
    
    Managed Instance sqatest-ol7-0 (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      kernel-tools-libs
          CVEs: CVE-2015-9289,    CVE-2017-17807,   CVE-2018-19985,   CVE-2018-20169,
                CVE-2018-7191,    CVE-2019-10207,   CVE-2019-10638,   CVE-2019-10639,
                CVE-2019-11190,   CVE-2019-11884,   CVE-2019-12382,   CVE-2019-13233,
                CVE-2019-13648,   CVE-2019-14283,   CVE-2019-15916,   CVE-2019-16746,
                CVE-2019-18660,   CVE-2019-3901,    CVE-2019-9503
      sqlite
          CVEs: CVE-2019-13734
      python
          CVEs: CVE-2018-20852,   CVE-2019-16056
      kernel-uek
          CVEs: CVE-2018-5953,    CVE-2019-18806,   CVE-2019-18809,   CVE-2020-10942
      python-libs
          CVEs: CVE-2018-20852,   CVE-2019-16056
      file
          CVEs: CVE-2018-10360
      file-libs
          CVEs: CVE-2018-10360
      curl
          CVEs: CVE-2019-5436
      libcurl
          CVEs: CVE-2019-5436
      mariadb-libs
          CVEs: CVE-2019-2737,    CVE-2019-2739,    CVE-2019-2740,    CVE-2019-2805
      kernel-tools
          CVEs: CVE-2015-9289,    CVE-2017-17807,   CVE-2018-19985,   CVE-2018-20169,
                CVE-2018-7191,    CVE-2019-10207,   CVE-2019-10638,   CVE-2019-10639,
                CVE-2019-11190,   CVE-2019-11884,   CVE-2019-12382,   CVE-2019-13233,
                CVE-2019-13648,   CVE-2019-14283,   CVE-2019-15916,   CVE-2019-16746,
                CVE-2019-18660,   CVE-2019-3901,    CVE-2019-9503
    
    Managed Instance sqatest-ol8-0 (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      sudo
          CVEs: CVE-2019-18634
      grub2-pc
          CVEs: CVE-2019-14865
      kernel
          CVEs: CVE-2019-15030,   CVE-2019-15031,   CVE-2019-18660,   CVE-2019-19527
      grub2-pc-modules
          CVEs: CVE-2019-14865
      sqlite-libs
          CVEs: CVE-2019-13734
      grub2-tools
          CVEs: CVE-2019-14865
      grub2-tools-minimal
          CVEs: CVE-2019-14865
      grub2-common
          CVEs: CVE-2019-14865
      bpftool
          CVEs: CVE-2019-15030,   CVE-2019-15031,   CVE-2019-18660,   CVE-2019-19527
      nss
          CVEs: CVE-2019-11745
      kernel-modules
          CVEs: CVE-2019-15030,   CVE-2019-15031,   CVE-2019-18660,   CVE-2019-19527
      libarchive
          CVEs: CVE-2019-18408
      grub2-tools-extra
          CVEs: CVE-2019-14865
      kernel-tools
          CVEs: CVE-2019-15030,   CVE-2019-15031,   CVE-2019-18660,   CVE-2019-19527
      nss-util
          CVEs: CVE-2019-11745
      kernel-tools-libs
          CVEs: CVE-2019-15030,   CVE-2019-15031,   CVE-2019-18660,   CVE-2019-19527
      nss-sysinit
          CVEs: CVE-2019-11745
    
    Managed Instance wb-zrh-cli-test (ocid1.instance.oc1.eu-zurich-1..<unique_ID>)
     has the following outstanding security patches:
      Security Intelligence Update for Windows Defender Antivirus - KB2267602 (Version 1.315.38.0)
      2020-04 Cumulative Update for Windows Server 2019 (1809) for x64-based Systems (KB4549949)