Amazon Web Services ブログ

機密性の高いネットワークデータを匿名化することで、保護された環境や規制された環境でMigration Evaluatorを使用する

この記事は、「Use Migration Evaluator in protected or regulated environments by anonymizing sensitive network data」を翻訳したものです。

Amazon Web Services (AWS)  Migration Evaluator は、VM(仮想マシン)の使用状況に関する詳細なデータを収集し、それを使ってクラウドへの移行に関するビジネスケースを作成し、移行コストを見積もることで、 VMware を利用している組織を支援することができます。しかし、システム名やIPアドレスなど、Migration Evaluator が収集した機密性の高いネットワークデータは、高度なセキュリティや規制のある組織から外部に持ち出すことはできません。このデータを手動で匿名化してアップロードすることは、規制対象の組織が Migration Evaluator を利用することを妨げるハードルとなっています。
このブログでは、シンプルな Python スクリプトで AWS Migration Evaluator の使用データを匿名化し、高度に規制された環境でもアップロードできるようにする方法をご紹介しています。これにより、情報セキュリティ登録査定者プログラム(IRAP)に見られるようなデータ主権や保管場所の要件を損なうことなく、Migration Evaluator の洞察を得ることができます。

Migration Evaluatorを使用する理由

データの匿名化は一つの方法ですが、Migration Evaluator から最大の価値を得るためには、匿名化を元に戻すことができる必要があります。例えば、プロセッサの使用量が少なく、ネットワークも使用されていない「ゾンビ」あるいは未使用の VM を特定するのに有効です。例えば、廃棄されなかったテストマシンや、もう存在しないチーム用のサーバーなどです。
オンプレミス環境の容量不足が原因で移行を希望している場合、未使用の VM を特定することで、容量を解放し、熟考された移行を行うための時間と余裕を組織内に与えることができます。
このシナリオを考えてみましょう。Migration Evaluator エージェントレスコレクタを実行した後、データファイルとして次のようなものが得られます。

図1. 顧客環境のVMの詳細を示すMigration Evaluatorの出力。

図1. Migration Evaluatorの出力が示す顧客環境のVMの詳細

このファイルを見ると、機密性が高い、VMの名前とアドレス情報が含まれていることが懸念されます。このファイルを Migration Evaluator のポータルにアップロードすると、内部ネットワークデータがローカルネットワーク外に出ないように要求するセキュリティポリシーに抵触する可能性があります。例えば、ヨーロッパ一般データ保護規則(GDPR)の対象となる業務や、オーストラリアの IRAP 準拠の環境では、このようなことが考えられます。
そこで、次のようにデータを匿名化します。

図2. Migration Evaluatorのデータファイルの機密情報を匿名化された値に置き換えたもの

すべての機密情報を削除した後、データを Migration Evaluator のポータルにアップロードして分析します。その結果、33% のサーバーがゾンビであることが判明しました。レポートを詳しく見ると、Server1 がもう使われておらず、廃棄できることがわかりました(図1の Migration Evaluator の出力ファイルでは「AvengersFileShare」と名付けられています)。これで、移行対象が 1/3 になり、オンプレミスのリソースが解放されました。
今度は、3 台のサーバーではなく、より現実的な 200 台または 2,000 台のサーバーがあると想像してみてください。このようなリストを手作業で匿名化するのは、時間がかかるだけでなく、エラーが発生する可能性もあります。

ソリューションの概要と Migration Evaluator の使用方法

このブログ記事で紹介されている手順とスクリプトは、規制の厳しい環境でも移行を進めるのに役立ちます。このスクリプトは、Migration Evaluator のオリジナルデータに含まれる機密性の高いフィールドを匿名化し、「PrimaryDC01」などの値を「Server0」に置き換えたり、IPアドレスをランダム化したりします。この匿名化されたファイルは、機密データを含んでいないため、AWSや地域外に送信することができます。しかし、データを匿名化するだけでなく、スクリプトは元の値と匿名化された値の両方を含む別のリバースマッピングファイルも生成します。
Migration Evaluator のレポートが生成されたとき、リバースマッピングファイルを検索して特定の VM を特定し、Migration Evaluator レポートの洞察を得ることができます。

前提条件

  1. 組織は、すでに移行評価を依頼し、「Migration Evaluator の使用を開始する」に従って Inventory Discovery ツールのいずれかをインストールしている必要があります。
  2. VM 環境で Migration Evaluation Collector を実行し、推奨期間(2週間)の使用データを収集済みであること。
  3. 自動データ同期を無効にしないと、変更されていないデータファイルが自動的に AWS にアップロードされます。

使用データおよびスクリプトの準備

まず、Migration Evaluator のデータを生成し、そのデータを csv ファイルとしてエクスポートし、テキストエディターを使って Python スクリプトを作成する必要があります。

  1. オンプレミスコレクターのシステム要件とインストールガイドのセクション 10、「Annotating Discovered Inventory with Business Data and Provisioning」の説明に従って、Migration Evaluator コレクターのインベントリーのエクスポートを生成します。この生成されたエクスポートは、Excel ワークブック (.xlxs) です。このワークブックには、環境から収集したシステム・データを含む 4 つのタブがあります。[Physical Provisioning]タブと[Virtual Provisioning]タブの両方に IP アドレスが含まれますが、4 つのタブすべてにシステム名が含まれます。
  2. Excel ドキュメントを開き、Excel の「ファイル/名前を付けて保存」メニューとファイルタイプ「カンマ区切り」を使用して、機密データを含む各タブを個別の csv ファイルにエクスポートします。csv ファイルの名前は重要ではありません。この手順では、「Physical_Provisioning.csv」、「Virtual_Provisioning.csv」、「Asset_Ownership.csv」、「Utilization.csv」が使用されます。
  3. csv ファイルを、Python インタープリターがインストールされているマシンのフォルダーにコピーします。このスクリプトは、Python のバージョン 2 または 3 の両方で動作します。
  4. テキストエディターを使って、同じフォルダに 「migr_eval_anon.py」という名前のファイルを作り、そこに以下のスクリプトをコピーペーストします。このとき、「# beginning of script」と 「# end of script」というコメントがあることを確認し、スクリプト全体が揃っていることを確認します。
# beginning of script
import csv
import sys
import os
from argparse import ArgumentParser

# Used to store each of the transformations that will be applied to a row in the data
class Transformation:
    def __init__(self, ix, prefix):
        self.col_index = ix
        self.prefix = prefix    

def ProcessArgs(argv):
    parser = ArgumentParser(description = """
    Script to process a CSV generated by the AWS Migration Evaluator tool and anonymise sensitive columns (e.g. system names and IPs)
    This script will generate artificial values for the anonymised columns in the output so that it can be sent to AWS without 
    exposing values. The original values are retained in the reverse mapping output file that allow the anonomysed values to be 
    converted back. If only Input File is specified, then anon and reverse map filenames are generated automatically. """)
    parser.add_argument("-if", "--input-file", dest="infile", default='Physical_Provisioning_Tab.csv',
                        help="File to use as source", metavar="FILE")
    parser.add_argument("-of", "--output-file", dest="outfile", default='',
                        help="File for output anonymised version", metavar="FILE")
    parser.add_argument("-rf", "--revmap-file", dest="revmapfile", default='',
                        help="File for output of dereference (original and anonomised) values", metavar="FILE")
    parser.add_argument('-c','--cols', nargs='+', dest="anoncols", default = ['Human Name', 'Server Name', 'Address'],
                        help='List of Column names to anonymise', metavar="COL")
    parser.add_argument('-p','--prefixes', nargs='+', dest="anonprefixes", default = ['SERVER', 'SERVER', 'IP'],
                        help='Prefix to use when generating anonymised values (IP generates anonymised IP Addresses)', metavar="COL")
    parser.add_argument('-id','--idcol', dest="idcol", default = 'Unique Identifier',
                        help='Name of Column with per system unique identifier for de-referencing', metavar="COL")
    args = parser.parse_args()    
    # Generate output filenames if empty (default)
    if (args.outfile == ''):
        args.outfile = os.path.splitext(args.infile)[0]+'_anon.csv'
    if (args.revmapfile == ''):
        args.revmapfile = os.path.splitext(args.infile)[0]+'_revmap.csv'
    return args

# Read a CSV Into a structure seperating out headers and data
def ReadCSV (filename):
    file_data = []
    file_headers = []
    with open(filename) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        line_count = 0
        for row in csv_reader:
            trans_row = row
            if line_count > 0:
                # save the first line (it's headers)
                file_data.append(row)
            else:
                #assign the transformed row to the data array
                file_headers = row
            line_count += 1
        print('Loaded row count: ' + str(line_count-1))
    
    result = {
        "headers": file_headers,
        "data": file_data
    }
    return result

# Take filename headers and a data array and create a CSV
def WriteCSV(filename, headers, rows):
    line_count = 0
    with open(filename, mode='w') as out_file:
        out_writer = csv.writer(out_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
        out_writer.writerow(headers)
        for row in rows:
            out_writer.writerow(row)
            line_count += 1
    print('Wrote row count: ' + str(line_count))

# Take a single row, the indexes and prefixes of which cols to anonymise and return a transformed version and the de-reference values
def TransformRow(row, rowidix, id, transforms):
    revmap_vals = [ row[rowidix] ]
    for tr in transforms:
        revmap_vals.append(row[tr.col_index]) # original value
        if (tr.prefix == "IP"): # special case for IP, convert count into 10.0.0.0/8 CIDR
            row[tr.col_index] = '10.' + str(int(id / 65536)) + '.' + str(int(id / 256) % 256) + '.' + str(id % 256)   
        else:
            row[tr.col_index] = tr.prefix + str(id)
        revmap_vals.append(row[tr.col_index]) # also include transformed value in same row for easy searching
    return row, revmap_vals

# Take the data, headers and apply transformation rules generating a new structure
def TransformData(filedata, anon_cols, rowid, prefixes):
    # First process headers and generate indexes for any of the columns we need to anonymise that are in the data
    rowidix = filedata['headers'].index(rowid)
    transforms = []
    revmap_headers = [rowid]
    i = 0
    for col in anon_cols:
        if (col in filedata['headers'] ):
            transforms.append(Transformation(filedata['headers'].index(col), prefixes[i]))
            revmap_headers.append(col)
            revmap_headers.append("anon_" + col)
        i = i + 1
    #Now anonymise the data 
    trans_array = []
    revmap_array = []
    line_count = 0
    for row in filedata['data']:
        trans_row = row
        trans_row, revmap_row = TransformRow(trans_row, rowidix, line_count, transforms)
        # Add to result ready for next
        trans_array.append(trans_row)
        revmap_array.append(revmap_row)
        line_count += 1
    print('Transformed row count: ' + str(line_count))
    result = {
        "headers": filedata['headers'],
        "data": trans_array,
        "revmapHeaders": revmap_headers,
        "revmap": revmap_array,
    }
    return result

# Simple Mainline. Process args, Read, Transform, then Write the two files.
args = ProcessArgs(sys.argv)
print ('Anonomysing Columns: ' + ','.join(args.anoncols))
readfile = ReadCSV(args.infile)
transfile = TransformData(readfile, args.anoncols, args.idcol, args.anonprefixes)
print ('Write anonomysed data to: ' + args.outfile)
WriteCSV(args.outfile, transfile['headers'], transfile['data'])
print ('Write reverse mapping data to: ' + args.revmapfile)
WriteCSV(args.revmapfile, transfile['revmapHeaders'], transfile['revmap'])
# end of script

機密データを削除する

上記のスクリプトを使用して、csv ファイルから機密データを削除する準備が整いました。デフォルトでは、このスクリプトは 「Human Name」 と「Server Name」という名前のカラムを取り、「SERVER1」などに置き換えます。また、「Address」カラムの値をすべて 10.X.X.X の IPアドレスに置き換えます。しかし、どのフィールドも匿名化することができ、必要に応じて別のコマンドラインパラメータを指定することで異なる接頭辞を使用することができます。

1. コマンドプロンプト/シェルを開き、スクリプトとcsvファイルのあるフォルダーを開きます。

2. csv ファイルに対してスクリプトを実行し、それぞれ匿名化ファイルと逆引きファイルを生成します。スクリプトは、処理された行数を表示します。
以下のコマンドを使用します。csvファイルのエクスポート時に異なる名前を使用した場合は、入力ファイル名を調整します。

python migr_eval_anon.py --input-file Physical_Provisioning.csv
python migr_eval_anon.py --input-file Virtual_Provisioning.csv
python migr_eval_anon.py --input-file Asset_Ownership.csv
python migr_eval_anon.py --input-file Utilization.csv

3. 元のcsv ファイルに加えて、それぞれ *_anon.csv と *_revmap.csv を用意しました。例:

図3. 生成された匿名化ファイルを示すコマンド出力画面。

図3. 生成された匿名化ファイルを示すコマンド出力のスクリーンショット

4. Migration Evaluator Collector からエクスポートした元の Excel ドキュメントを開き、新しいコピーを保存します。

5. Physical_Provisioning_anon.csv をExcel で開き、シート全体を選択して、すべてのデータをコピーします。次に、タブ全体を選択し、[編集/形式を選択してペースト/テキスト]を使用して匿名化されたデータを貼り付け、[Physical Provisioning]タブの内容を置き換えます。機密データのないExcel ワークブックができあがるまで、各 csv ファイルとタブに対してこの作業を繰り返します。

6. *_revmap.csv ファイルは、元の Excel ワークブックと一緒に、ローカルマシンまたはファイル共有に安全に保管してください。Migration Evaluator レポートが作成されたら、*_revmap.csv ファイルにある元のシステム名を使用して、特定のシステムを識別することができます。

レビューとアップロード

この時点で、Migration Evaluator Management Console にアップロード可能なExcel ワークブックが完成しました。組織固有のセキュリティルールによっては、ワークブックのレビューが必要な場合があります。これで Migration Evaluator のレポートを取得し、インフラストラクチャがホストしているゾンビVM の数を確認することができるようになりました。
Migration Evaluator は、ゾンビVM を特定する以上のことを行うことができます。また、AWS への移行にかかるコストの見積もりと、そのコストを標準的なオンプレミス環境と比較したビジネスケースを作成することができます。
詳細はAWS Migration Evaluatorのメインページをご覧ください。また、インフラの適切なサイジングがどのように組織や企業のコスト削減を支援するかについてもお読みください。


AWS Public Sector Blog ニュースレターを購読して、公共部門のための AWS のツール、ソリューション、イノベーションの最新情報を受け取るか、お問い合わせください。
AWS Public Sector Blog はあなたの助けを必要としています。このアンケートでは、AWS Public Sector Blog での体験に関する感想を数分で共有いただけます。アンケートからのフィードバックは、読者の好みに沿ったより多くのコンテンツを作成するために使用いたします。


原文著者

Cain Hopwood

Cain Hopwood

Cain Hopwood は Amazon Web Services (AWS)  のオーストラリア公共部門チームのソリューションアーキテクトです。20 年以上のソフトウェアエンジニアリングの経験を持ち、特にAPI駆動型ソフトウェアアーキテクチャと人工知能(AI)ベースのソリューションの構築を支援することに喜びを感じています。余暇には、スキーやマウンテンバイク、SF小説を執筆しています。彼の著書は、Amazon や Kindle でご覧いただけます。

翻訳は Solutions Architect 小本 が担当しました。