Migrate Target Groups to Tags

avatar guyavatar guy
By Casey Reid; Director, Security Engineering


Tenable deprecated target groups in Tenable.io on February 4th, 2022. Tenable recommends that customers use tags instead to group and scan assets in Tenable.io.



For a quick demonstration of asset tagging, see the Understanding Asset Tagging in Tenable.io video on the Tenable Product Education YouTube channel.

Instead of manually migrating all of your target groups to tags, you can utilize the Tenable.io API and pyTenable to perform the migration automatically. This article outlines the steps required to migrate target groups to tags using pyTenable to interact with the Tenable.io API. Each line of Python code is explained in depth.



Tenable recommends that customers migrate their target groups to tags as explained in this expert article. However, if your organization exceeds the tag value limit of 1,024 objects you can migrate your target groups to text targets instead. For more information, see Migrate Target Groups to Text Targets.

To illustrate the automation we will use an example system target group called My Assets that has a number of IPs and subnets defined in the targets. Our goal is to grab all the IPs and subnets from the My Assets target group and create a new tag with the same IPs and subnets. A tag requires both a category and a value so we will use the target group's type as the category and the name as the value.

For example, the system target group My Assets will use system as the tag category and My Assets as the tag value.

Target GroupTarget Group

Before You Begin

This article assumes that you have learned how to authenticate to the Tenable.io API using pyTenable as explained in the Introduction to pyTenable article. Additionally, the examples provided below use an instantiated pyTenable object called tio as described in the Introduction to pyTenable article.

Automate the Migration

List All Target Groups

Our first step is to list all target groups in the Tenable.io instance and save the values. pyTenable can return a list of target groups with a simple for loop. For each target group listed in the JSON response, we can save its value into a variable called tgroup.

for tgroup in tio.target_groups.list():

Below is a single record from the response after running the code above. We will use the name, type, and members fields to create our future tag.

  "acls": [
      "permissions": 32,
      "owner": "None",
      "display_name": "None",
      "name": "None",
      "id": "None",
      "type": "default"
      "permissions": 128,
      "owner": 1,
      "display_name": "system",
      "name": "nessus_ms_agent",
      "id": 1,
      "type": "user"
  "default_group": 0,
  "type": "system",
  "members": ",,,,",
  "name": "My Assets",
  "owner": "nessus_ms_agent",
  "shared": 1,
  "user_permissions": 32,
  "last_modification_date": 1645331720,
  "creation_date": 1645331719,
  "owner_id": 1,
  "id": 5652

Create Variables for Name, Members, and Type

Next we need to create variables for the name, type, and members fields of each target group. These variables will help us create our pending tags. We can print out the variables to ensure we haven't made any mistakes.

for tgroup in tio.target_groups.list():
   member = tgroup['members']
   name = tgroup['name']
   group_type = tgroup['type']

   print(name, member, group_type)

Here is an an example response from the code above:

My Assets,,,, system

In the output, we can see the name of the target group (My Assets), its members (,,,,, and the target group type (system). We now have everything we need to create our Tags via the API with pyTenable.

Tag Description

When creating a tag in the user interface, you have the ability to include a description. We can utilize this description field to indicate that the tags were automatically created with a script.

description = "Imported by <script name>."

Note that this step is optional, it isn't required by the API or pyTenable but is a good practice to use with automation.

Error Prevention

To prevent the script from breaking when we encounter a name conflict, we can use a try block. Additionally, we want to avoid recreating the default target group since it isn't needed. To accomplish this, we can use an if statement and the not equals operator != against the string Default.

Note that this is a case-sensitive string comparison so using default would fail.

    if name != 'Default':

Create the Tags

pyTenable makes the creation of tags extremely simple by allowing us to call a function and submit the values needed for the tag creation. We can call the tags function in pyTenable as shown below:

tio.tags.create(group_type, name, description, filters=[("ipv4", "eq", str(member))])

In the code sample above, filters are a list of tuples in the form of (FIELD, OPERATOR, VALUE). Since we are migrating target groups we know that the field will always be ipv4 or ipv6. Also note that member is cast as a string to avoid / in subnets. For more information about working with tags in pyTenable, see Tags in the pyTenable documentation.



For information on how to instantiate the tio object, see the Introduction to pyTenable article.

Next, we can finish our try block with an except and a message to alert the user when a duplicate tag is found. A duplicate tag should only be found if the script was previously executed since it's unlikely that you will have a tag category named system or user with the exact name of a previously created target group. pyTenable returns an API error when a naming conflict is encountered but we can skip this error by using pass.

   print("\nDuplicate Tag found. Skipping\n")

Full Code Solution

Finally, we've arrived at the finish line! The full code sample to migrate target groups to tags using pyTenable is illustrated below:

from tenable.io import TenableIO

access_key = 'ACCESS_KEY_GOES_HERE'
secret_key = 'SECRET_KEY_GOES_HERE'

tio = TenableIO(access_key, secret_key, vendor='Casey Reid', product='Migrate Target Groups to Tags', build='0.0.1')

for tgroup in tio.target_groups.list():
    member = tgroup['members']
    name = tgroup['name']
    group_type = tgroup['type']

    description = 'Migrated from target groups via a script.'

        if name != 'Default':
            tio.tags.create(group_type, name, description, filters=[("ipv4", "eq", str(member))])
        print("\nDuplicate Tag found. Skipping\n")

After Migration

In the example above, we didn't adjust the scans that have target groups assigned and move them to their newly created tag counterpart. This is due to a current limitation in the Tenable.io API that limits the selection of the Targets defined by tags option. This means that you will need to manually adjust your scans to point to the new tags that were just created. If the tag is defined as a target like the example above, you will need to select so in the drop down box as shown in the screenshot below:

Targets defined by tagsTargets defined by tags


If you are not a developer and the pyTenable example above was overwhelming, you can accomplish the same task with Navi. After downloading and installing Navi via Python's package manager (pip), you can migrate target groups to tags with two commands:

navi keys -–a <access key> -–s <secret key>
navi tgroup migrate –tags

For more information about Navi, see the Navi GitHub wiki.

For More Information

Did this page help you?