{"id":37092498,"url":"https://github.com/newrelic/nr-entity-tag-sync","last_synced_at":"2026-01-14T11:15:30.666Z","repository":{"id":166950047,"uuid":"630492260","full_name":"newrelic/nr-entity-tag-sync","owner":"newrelic","description":"The New Relic Entity Tag Sync application is a tool used to map entity metadata from entities in an external system of record to tags on New Relic entities and to keep such tag values synchronized with the external entity metadata values across time.","archived":false,"fork":false,"pushed_at":"2025-04-16T22:15:42.000Z","size":147,"stargazers_count":6,"open_issues_count":5,"forks_count":5,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-09-29T15:56:20.884Z","etag":null,"topics":["cmdb","nrlabs","nrlabs-utility","servicenow"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/newrelic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-20T13:52:38.000Z","updated_at":"2025-03-11T13:00:46.000Z","dependencies_parsed_at":"2024-11-08T19:24:48.231Z","dependency_job_id":"d80065e7-623c-432b-a3ba-2b504d7eeef7","html_url":"https://github.com/newrelic/nr-entity-tag-sync","commit_stats":null,"previous_names":["newrelic/nr-entity-tag-sync"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/newrelic/nr-entity-tag-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newrelic%2Fnr-entity-tag-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newrelic%2Fnr-entity-tag-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newrelic%2Fnr-entity-tag-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newrelic%2Fnr-entity-tag-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/newrelic","download_url":"https://codeload.github.com/newrelic/nr-entity-tag-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newrelic%2Fnr-entity-tag-sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28418057,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:47:48.104Z","status":"ssl_error","status_checked_at":"2026-01-14T10:46:19.031Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cmdb","nrlabs","nrlabs-utility","servicenow"],"created_at":"2026-01-14T11:15:29.692Z","updated_at":"2026-01-14T11:15:30.652Z","avatar_url":"https://github.com/newrelic.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Community Project header](https://github.com/newrelic/open-source-office/raw/master/examples/categories/images/Community_Project.png)](https://github.com/newrelic/open-source-office/blob/master/examples/categories/index.md#category-community-project)\n\n# New Relic Entity Tag Sync\n\n\n![GitHub forks](https://img.shields.io/github/forks/newrelic/nr-entity-tag-sync?style=social)\n![GitHub stars](https://img.shields.io/github/stars/newrelic/nr-entity-tag-sync?style=social)\n![GitHub watchers](https://img.shields.io/github/watchers/newrelic/nr-entity-tag-sync?style=social)\n\n![GitHub all releases](https://img.shields.io/github/downloads/newrelic/nr-entity-tag-sync/total)\n![GitHub release (latest by date)](https://img.shields.io/github/v/release/newrelic/nr-entity-tag-sync)\n![GitHub last commit](https://img.shields.io/github/last-commit/newrelic/nr-entity-tag-sync)\n![GitHub Release Date](https://img.shields.io/github/release-date/newrelic/nr-entity-tag-sync)\n\n![GitHub issues](https://img.shields.io/github/issues/newrelic/nr-entity-tag-sync)\n![GitHub issues closed](https://img.shields.io/github/issues-closed/newrelic/nr-entity-tag-sync)\n![GitHub pull requests](https://img.shields.io/github/issues-pr/newrelic/nr-entity-tag-sync)\n![GitHub pull requests closed](https://img.shields.io/github/issues-pr-closed/newrelic/nr-entity-tag-sync)\n\n## Overview\n\nThe New Relic Entity Tag Sync application is a tool used to map entity metadata\nfrom entities in an external system of record to tags on New Relic entities and\nto keep such tag values synchronized with the external entity metadata values\nacross time.\n\n### Concepts\n\nThere are several key concepts to be aware of in order to understand how the\nentity tag sync application works.\n\n#### New Relic Entities\n\nA New Relic entity is anything that reports data to New Relic or that\ncontains data that we have access to. More information on New Relic entities\nis available [in our documentation](https://docs.newrelic.com/docs/new-relic-solutions/new-relic-one/core-concepts/what-entity-new-relic/).\n\n#### External Entities\n\nAn external entity can be generally thought of as any object with metadata in\nan external system of record. The specific definition of an external entity is\nparticular to the external system.\n\nWithin the entity tag sync application, external entities are modeled as a set\nof key-value pairs and a unique ID. It is these key-value pairs that are used\nduring the synchronization process to populate and update tags on the New Relic\nentities.\n\n#### Providers\n\nA provider is a module capable of retrieving entity metadata from an external\nsystem of record and mapping that data to the internal representation of an\nexternal entity as a set of key-value pairs.\n\nThe following providers are supported:\n\n* ServiceNow CMDB\n\n##### ServiceNow CMDB provider\n\nThe ServiceNow CMDB provider models CMDB configuration items (CIs) as external\nentities, enabling fields on CIs to be mapped to tags on New Relic entities.\n\nThe ServiceNow CMDB provider leverages the\n[ServiceNow ReST API](https://docs.servicenow.com/bundle/utah-api-reference/page/integrate/inbound-rest/concept/c_RESTAPI.html)\nto retrieve CI data and supports both HTTP Basic authentication and OAuth 2.0\nauthentication. See the [ReST API Security](https://docs.servicenow.com/bundle/utah-api-reference/page/integrate/inbound-rest/concept/c_RESTAPI.html#d773849e666)\ndocumentation for more details on using these authentication methods with the\nServiceNow ReST API. See the [ServiceNow CMDB provider parameters section](#servicenow-cmdb-provider-parameters)\nfor details on how to configure the ServiceNow CMDB provider to use these\nauthentication methods.\n\n#### Mappings\n\nMappings drive the actual synchronization process. Each mapping tells the entity\ntag sync application what external entities to select from the provider, what\nentities to select from New Relic, how to match external entities with New Relic\nentities, and how key-value pairs from the external entity map to tags on the\nNew Relic entity.\n\nMappings are specified in the [mappings](#mapping-parameters) section of\n[the configuration file](#configuration). During the synchronization process,\nthe entity tag sync application processes each mapping in order. A mapping is\nprocessed as follows.\n\n1. Fetch external entities\n\n   All external entities matching the [external entity query criteria](#external-entity-query-criteria)\n   are retrieved via the provider. The keys of the [`mapping`](#mapping) node as\n   well as the value of the `extEntityKey` in the [`match`](#match-strategy)\n   node are passed to the provider indicating the key-value pairs to retrieve\n   for each external entity. The [last update timestamp](#delta-synchronization)\n   is also passed to the provider if one was retrieved.\n\n2. Fetch New Relic entities\n\n   All New Relic entities matching the [New Relic entity query critera](#new-relic-entity-query-criteria)\n   are retrieved via the New Relic GraphQL API\n\n3. Find matching entities\n\n   For each selected New Relic entity, the [match strategy](#match-strategy) is\n   applied to find a matching external entity. If no match is found, processing\n   proceeds to the next New Relic entity or, if all candidate New Relic entities\n   have been processed, to the next mapping. If a match is found, processing\n   continues to the synchronize step.\n\n4. Synchronize tags\n\n   If a match is discovered, the synchronization process executes the following\n   logic for each pair of external entity key, **E**, to New Relic tag name,\n   **T** in the [`mapping`](#mapping) node.\n\n   - If **E** *does not* exist in the external entity metadata and **T**\n     *does not* exist in the tags of the matching New Relic entity, do nothing\n     and continue to the next pair.\n   - If **E** *does not* exist in the external entity metadata and **T** *does*\n     exist in the tags of the matching New Relic entity, delete the tag **T**\n     from the New Relic entity.\n   - If **E** *does* exist in the external entity metadata and **T** *does not*\n     exist in the tags of the matching New Relic entity, add a tag to thew New\n     Relic entity with **E** for the tag name and the value of key **E** in the\n     external entity metadata as the singular tag value.\n   - If **E** *does* exist in the external entity metadata and **T** *does*\n     exist in the tags of the matching New Relic entity, scan the values for the\n     tag **T** in the matching New Relic entity.\n     - If the value of key **E** in the external entity metadata *is* in the\n       values of tag **T**, do nothing and continue to the next pair.\n     - If the value of key **E** in the external entity metadata *is not* in the\n       values of tag **T**, the values of tag **T** are **replaced** with the\n       value of key **E** in the external entity metadata.\n\n   **NOTE:** Case 2 and case 4, subcase 2 above are destructive. In both cases,\n   existing tag values removed. In general it should probably be assumed that\n   the tags being synchronized are managed by the entity tag sync application.\n   and should be modified via other means.\n\n### Audit Events\n\nThe entity tag sync application is capable of producing audit events at various\npoints during the synchronization process. This feature is disabled by default\nbut can be enabled by setting the `events.enabled`\n[general configuration parameter](#general-parameters). When enabled, the entity\ntag sync application will produce events with the event type specified in the\n`events.eventType` [general configuration parameter](#general-parameters) or the\nevent type `EntityTagSync` by default. The following attributes and values are\ncaptured for _every_ event. Additional attributes are [action](#event-actions)\ndependent.\n\n| Name | Type | Description |\n| --- | --- | --- |\n| `id` | string | A canonical RFC-4122 UUID string that uniquely identifies each synchronization cycle |\n| `action` | string | A string identifying the [action](#event-actions) that this event describes |\n| `error` | bool | Flag indicating if an error occurred during this transaction or not |\n| `errorMessage` | string | If an error occurred, a message describing what happened |\n\n#### Event Actions\n\nThe following `action`s are produced along with any additional attributes\ncaptured by each action.\n\n**sync_start**\n\nThis action is produced at the start of each sync cycle and does not carry any\nadditional attributes. Note that the `error` attribute for this action will\nalways be `false` and the `errorMessage` attribute  will always be empty.\n\n**sync_end**\n\nThis action is produced at the end of each sync cycle and does not carry any\nadditional attributes. The `error` attribute for this action will be set to\n`true` if _any_ error occurred, including the case where the sync cycle finishes\nsuccessfully but a specific mapping has update errors. The `errorMessage`\nattribute will be set providing more details.\n\n**mapping_complete**\n\nThis action is produced each time during a sync cycle that the entity tag sync\napplication finishes processing a [mapping](#mappings). The set of attributes\ncaptured for this action fall into one of three cases.\n\n1. If an error occurred while processing the mapping, The `error` attribute for\n   this action will be set to `true`, the `errorMessage` attribute will be set\n   providing more details, and no additional attributes will be present.\n1. If no external entities were returned by the [provider](#providers), not due\n   to an error, the `error` attribute for this action will be `false`, the\n   `errorMessage` attribute will be empty, and the `extEntityCount` attribute\n   will be set to `0`, indicating that no external entities were returned and\n   the subsequent mapping process was skipped since there was nothing to do.\n1. If the overall mapping process completed, even if there were errors updating\n   _some_ entities, the `error` attribute for this action will be `false`, the\n   `errorMessage` attribute will be empty and the following attributes will be\n   set.\n\n    * `extEntityCount` - the number of external entities returned by the\n      [provider](#providers)\n    * `totalEntityCount` - the total number of New Relic entities that matched\n      the [New Relic entity criteria](#new-relic-entity-query-criteria)\n    * `totalEntitiesScanned` - the total number of New Relic entities that were\n      tested against the external entities using [the match strategy](#match-strategy).\n      This might be different than the `totalEntityCount` if an error occurred\n      before all entities could be processed.\n    * `totalEntitiesMatched` - the total number of New Relic entities that\n      matched an external entity according to [the match strategy](#match-strategy)\n    * `totalEntitiesNoMatch` - the total number of New Relic entities that did\n      not match an external entity according to [the match strategy](#match-strategy)\n    * `totalEntitiesSkipped` - the total number of New Relic entities that\n      matched an external entity according to [the match strategy](#match-strategy)\n      but were up-to-date with the external entity and did not require updates\n    * `totalEntitiesUpdated` - the total number of New Relic entities that\n      matched an external entity according to [the match strategy](#match-strategy)\n      and were updated successfully\n    * `totalEntitiesWithErrors` - the total number of New Relic entities that\n      matched an external entity according to [the match strategy](#match-strategy)\n      but were not updated successfully due to errors\n\n### Delta Synchronization\n\nBy default, the synchronization cycle is stateless. As a result, unless\nimplemented directly by the provider, the provider has no way to determine when\nthe last synchronization was run which will likely cause the provider to\nretrieve all external entities during each synchronization cycle. This may be\nexpensive in terms of consumed resources, especially as the data set of external\nentities and/or the number of key-value pairs being retrieved increase.\n\nTo address this issue, the entity tag sync application can be configured to\n\"maintain\" the timestamp of the last synchronization and pass the timestamp to\nthe provider when retrieving external entities. To enable this feature, the\n`provider.useLastUpdate` flag must be set to `true` and [audit events](#audit-events)\nmust be enabled.\n\nWhen enabled, the entity tag sync application will query NRDB for the latest\noccurence of the [audit event](#audit-events) with the event type specified in\nthe `events.eventName` configuration parameter for which the value of the\n`action` attribute is set to `sync_end`. The resulting timestamp is passed to\nthe provider implementation as the third parameter of the\n[`GetEntities`](https://github.com/newrelic/nr-entity-tag-sync/blob/main/internal/provider/provider.go#L17)\nfunction.\n\nProvider implementations are not required to support this feature but providers\nthat do support it must honor it when it is passed.\n\n**NOTE:** This functionality is currently implemented in a fairly primitive way.\nThe timestamp of the last synchronization is determined by querying NRDB for the\nlatest timestamp of the most recent audit event. This is why\n[audit events](#audit-events)must be enabled in order to leverage this feature.\n\n## Getting Started\n\nThe New Relic Entity Tag Sync application can be run directly [on a host](#on-host)\nor [run as an AWS Lambda function](#aws-lambda-function).\n\n### On-host\n\nThe New Relic Entity Tag Sync application provides binaries for the following\nhost environments.\n\n* [Linux x86](https://github.com/newrelic/nr-entity-tag-sync/releases/latest/download/nr-entity-tag-sync_Linux_i386.tar.gz)\n* [Linux amd64](https://github.com/newrelic/nr-entity-tag-sync/releases/latest/download/nr-entity-tag-sync_Linux_x86_64.tar.gz)\n* [Windows x86](https://github.com/newrelic/nr-entity-tag-sync/releases/latest/download/nr-entity-tag-sync_Windows_i386.zip)\n* [Windows amd64](https://github.com/newrelic/nr-entity-tag-sync/releases/latest/download/nr-entity-tag-sync_Windows_x86_64.zip)\n\n#### Run the application on host\n\nTo run the New Relic Entity Tag Sync application as a standalone application on\na host, perform the following steps.\n\n1. Download the binary for your platform from\n   [the latest release](https://github.com/newrelic/nr-entity-tag-sync/releases/latest)\n1. Extract the archive to a new or existing directory.\n1. Create a directory named `configs` in the same directory.\n1. To use an existing configuration file, copy it to a file named [`config.yml`](#configuration)\n   in the new `configs` directory. To create a new configuration file, copy the\n   [`configs/config.sample.yml`](./configs/config.sample.yml).\n   to `configs/config.yml` and customize it to [configure](#configuration) the\n   application appropriately for your environment.\n1. Optionally, to specify one or more [configuration](#configuration) parameters\n   using environment variables, set the appropriate environment variables.\n1. From the directory where the archive was extracted, execute the integration\n   binary using the command `./nr-entity-tag-sync` (or\n   `.\\nr-entity-tag-sync.exe` on Windows).\n\n### AWS Lambda Function\n\nTo run the New Relic Entity Tag Sync application as an [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html)\nfunction, create an AWS Lambda function using one of the following methods.\n\n* [Use the included deployment scripts](#deploy-using-the-deployment-scripts)\n* [Integrate with an existing provisioning process](#deploy-using-an-existing-provisioning-process)\n\n#### Requirements for running the AWS Lambda Function\n\n* The Entity Tag Sync Lambda function requires an execution role that the AWS\n  Lambda service can assume to run the Lambda function. Either an existing role\n  can be used or a new role can be created.\n\n  When creating a new role, the Entity Tag Sync Lambda function does not need\n  access to any AWS services other than [CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html)\n  (to write log events). Attaching the\n  [AWSLambdaExecute](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaExecute.html)\n  managed policy or the [AWSLambdaBasicExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html)\n  managed policy or the equivalent thereof, is sufficient.\n\n#### Deploy using the deployment scripts\n\nA [deployment script](./scripts/lambda/deploy.sh) is included that can be run\nusing the included [`Makefile`](./Makefile) to build the [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html)\nand deploy the Entity Tag Sync Lambda function as part of a new\n[AWS Stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html)\nin one step.\n\n[The provided CloudFormation template](./deployments/lambda/cf-template.yaml)\nwill create a new [AWS Stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html)\nwith four resources.\n\n1. The Entity Tag Sync Lambda function\n1. An [EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)\n   [schedule](https://docs.aws.amazon.com/eventbridge/latest/userguide/using-eventbridge-scheduler.html)\n   that will invoke the Entity Tag Sync Lambda function every 15 minutes\n   starting at the beginning of each hour\n1. An [EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)\n   schedule group to contain the schedule\n1. An [IAM role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)\n   that the [EventBridge scheduler](https://docs.aws.amazon.com/eventbridge/latest/userguide/using-eventbridge-scheduler.html)\n   can assume to execute the Entity Tag Sync Lambda function\n\nThe only _required_ resource in this [AWS Stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html)\nis the Entity Tag Sync Lambda function. The [EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)\nresources and the [IAM role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)\ncan be removed from the template if existing resources will be used and/or if\nthe Entity Tag Sync Lambda function will be invoked as the target of a different\nresource.\n\n[The provided CloudFormation template](./deployments/lambda/cf-template.yaml)\nleverages [CloudFormation template parameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html)\nto customize various properties of the created resources. A [sample parameters file](./deployments/lambda/cf-params.sample.json)\nis provided that shows an example of each parameter that can be used with the\nCloudFormation template.\n\n**NOTE:** Deploying the Entity Tag Sync Lambda function using the deployment\nscripts requires the [AWS CLI](https://aws.amazon.com/cli/) to be installed on\nthe same system where the repository was cloned. It also requires an S3 bucket\nwhere the [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html)\ncan be uploaded so that the [CloudFormation template](./deployments/lambda/cf-template.yaml)\ncan reference it to during deployment. Either an existing bucket can be used or\na new bucket can be created.\n\nTo deploy the Entity Tag Sync Lambda function using this method, perform the\nfollowing steps.\n\n1. Clone this repository using [`git clone`](https://git-scm.com/docs/git-clone).\n1. Navigate to the repository root.\n1. To use an existing configuration file, copy it to a file named [`config.yml`](#configuration)\n   in the `configs` directory. To create a new configuration file, copy the\n   [`configs/config.sample.yml`](./configs/config.sample.yml) to\n   `configs/config.yml` and customize it to [configure](#configuration) the\n   application appropriately for your environment.\n1. Copy the [`deployments/lambda/cf-params.sample.json`](./deployments/lambda/cf-params.sample.json)\n   to `deployments/lambda/cf-params.json`.\n1. Use the parameter descriptions in the CloudFormation template at\n   [`deployments/lambda/cf-template.yaml`](./deployments/lambda/cf-template.yaml)\n   as a guide to update the template parameters in\n   `deployments/lambda/cf-params.json`.\n1. Ensure that appropriate [authentication and access credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-authentication.html)\n   are set to allow the AWS CLI to authenticate.\n1. Run the command `AWS_S3_BUCKET_NAME=my-s3-bucket make deploy-lambda`, where\n   `my-s3-bucket` is the same as the value of the `S3BucketName` template\n   parameter that is set in the `deployments/lambda/cf-params.json` file.\n1. Verify that the command ran successfully by looking for the following console\n   output.\n\n```bash\n...\nBuilding lambda zip package...\n  adding: bootstrap (deflated 51%)\n  adding: configs/ (stored 0%)\n  adding: configs/config.yml (deflated 43%)\nUploading lambda zip package...\nupload: ../nr-entity-tag-sync.zip to s3://YOUR_BUCKET_NAME/nr-entity-tag-sync.zip\nDeploying stack nr-entity-tag-sync...\n\nWaiting for changeset to be created..\nWaiting for stack create/update to complete\nSuccessfully created/updated stack - nr-entity-tag-sync\nDone.\n```\n\n##### Update or uninstall using the deployment scripts\n\nAn [update script](./scripts/lambda/update.sh) is included that can be run using\nthe included [`Makefile`](./Makefile) to update the Entity Tag Sync Lambda\nbinary or the [`config.yml`](#configuration) file. This can be useful to update\nto the latest version of the Entity Tag Sync application or deploy a new [`config.yml`](#configuration)\nfile.\n\nTo update to the latest version of the Entity Tag Sync application, perform the\nfollowing steps.\n\n1. Navigate to the repository root.\n1. Update your repository to the latest version using [`git pull`](https://git-scm.com/docs/git-pull),\n   [`git rebase`](https://git-scm.com/docs/git-rebase), etc.\n1. Run the command `AWS_S3_BUCKET_NAME=my-s3-bucket make update-lambda`, where\n   `my-s3-bucket` is the name of an S3 bucket to upload the\n   [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html)\n   to.\n1. Verify that the command ran successfully by looking for the following console\n   output.\n\n```bash\n...\nBuilding lambda zip package...\n  adding: bootstrap (deflated 51%)\n  adding: configs/ (stored 0%)\n  adding: configs/config.yml (deflated 43%)\nUploading lambda zip package...\nupload: ../nr-entity-tag-sync.zip to s3://YOUR_BUCKET_NAME/nr-entity-tag-sync.zip\nDone.\n```\n\nTo deploy a new [`config.yml`](#configuration), perform the following steps.\n\n1. Navigate to the repository root.\n1. Copy the new [`config.yml`](#configuration) file to the  `configs`\n   directory or update the existing [`config.yml`](#configuration) file in the\n   `configs` directory.\n1. Run the command `AWS_S3_BUCKET_NAME=my-s3-bucket make update-lambda`, where\n   `my-s3-bucket`  is the name of an S3 bucket to upload the\n   [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html)\n   to.\n1. Verify that the command ran successfully by looking for the console output\n   shown above.\n\nAn [uninstall script](./scripts/lambda/delete.sh) is also included that can be\nrun using the included [`Makefile`](./Makefile) to remove the Entity Tag Sync\n[AWS Stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html)\nthat was deployed using the [deploy script](./scripts/lambda/deploy.sh).\n\nTo uninstall the Entity Tag Sync [AWS Stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html),\nperform the following steps.\n\n1. Navigate to the repository root.\n1. Run the command `make delete-lambda`.\n1. Verify that the command ran successfully by looking for the following console\n   output.\n\n```bash\n...\nDeleting stack nr-entity-tag-sync...\nWaiting for stack delete to complete...\nDone.\n```\n\n#### Deploy using an existing provisioning process\n\nTo use an existing provisioning process such as [CloudFormation](https://aws.amazon.com/cloudformation/),\n[Terraform](https://www.terraform.io/), or [Ansible](https://docs.ansible.com/)\nto deploy the Entity Tag Sync Lambda, build the [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html)\nand use the appropriate mechanisms of the provisioning tool(s) to create an\n[AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html)\nfunction with the properties shown below.\n\nTo build the [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html)\nperform the following steps.\n\n1. Clone this repository using [`git clone`](https://git-scm.com/docs/git-clone).\n1. Navigate to the repository root.\n1. To use an existing configuration file, copy it to a file named [`config.yml`](#configuration)\n   in the `configs` directory. To create a new configuration file, copy the\n   [`configs/config.sample.yml`](./configs/config.sample.yml) to\n   `configs/config.yml` and customize it to [configure](#configuration) the\n   application appropriately for your environment.\n1. Run the command `make package-lambda`.\n1. Verify that the command ran successfully by looking for the following console\n   output.\n\n```bash\n...\nBuilding lambda zip package...\n  adding: bootstrap (deflated 51%)\n  adding: configs/ (stored 0%)\n  adding: configs/config.yml (deflated 43%)\n```\n\nOn successful completion of the `make` command, the deployment package will be\nlocated at `dist/nr-entity-tag-sync.zip`. Use this deployment package\nalong with the following property values to create the AWS Lambda function.\n\n* Lambda package type: `Zip`\n* Lambda deployment package: Reference to the location of the generated\n  deployment package ZIP file\n* Lambda entry point/handler name: `bootstrap`\n* Lambda runtime identifier: `provided.al2023`\n* Lambda environment variables: Specify [configuration](#configuration)\n  parameters that should be set using environment variables.\n\n#### Run the application as an AWS Lambda Function\n\nThe application can be run using any supported [invocation method](https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html).\nFor example, when deploying the AWS Lambda function using [the provided CloudFormation template](./deployments/lambda/cf-template.yaml),\nan [AWS EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)\n[schedule](https://docs.aws.amazon.com/eventbridge/latest/userguide/using-eventbridge-scheduler.html)\nis also created to invoke the AWS Lambda function on a specified schedule.\n\n## Usage\n\n### Configuration\n\nThe Entity Tag Sync application is driven by a YAML configuration file. The\nconfiguration file consists of a set of [general parameters](#general-parameters),\na set of [provider parameters](#provider-parameters), and an array of\n[mappings](#mappings). [A sample configuration file](configs/config.sample.yml)\nis provided that shows an example of all parameters.\n\n#### General parameters\n\nThe following general configuration parameters are supported. Some parameters\ncan be specified as environment variables as indicated below. Parameters listed\nbelow with dots in their names correspond to nested YAML structures. For example\n`log.level` corresponds to the following YAML.\n\n```yaml\nlog:\n  level: warn\n```\n\n| Name | Environment Variable | Description | Required | Example | Default |\n| --- | --- | --- | --- | --- | --- |\n| `apiKey` | `NEW_RELIC_API_KEY` | A New Relic User API key | Y | `NRAK-123456` | |\n| `licenseKey` | `NEW_RELIC_LICENSE_KEY` | A New Relic Ingest License Key used for the Event API | Y if events enabled | `123456NRAL` | |\n| `region` | `NEW_RELIC_REGION` | The New Relic datacenter to access (`US` or `EU`) | N | `US` | `US` |\n| `log.level` | | The application log level | N | `debug` | `warn` |\n| `log.fileName` | | Log file name | N | `app.log` | Standard output |\n| `events.enabled` | | Flag to enable [audit event](#audit-events) | N | `true` | `false` |\n| `events.accountId` | `NEW_RELIC_ACCOUNT_ID` | New Relic account where [audit events](#audit-events) are posted | Y if events enabled | `12345` | |\n| `events.eventName` | | Name of [audit event](#audit-events) type | N | `MyCustomTagSyncEvent` | `EntityTagSync` |\n\n#### Provider parameters\n\nThe `provider` section of the configuration file is used to specify the\nparameters for the external entity provider. This section contains common\nparameters that are provider indepent and parameters that are specific to the\nselected provider. The following common parameters are supported.\n\n| Name | Description | Required | Example | Default |\n| --- | --- | --- | --- | --- |\n| `type` | The provider implementation to use | Y | `servicenow` | |\n| `useLastUpdate` | Flag to enable [delta synchronization](#delta-synchronization) | N | `true` | `false` |\n\nThe following values for the `type` parameter are supported.\n\n* [`servicenow`](#servicenow-cmdb-provider-parameters)\n\n##### ServiceNow CMDB provider parameters\n\nThe ServiceNow CMDB provider supports the following configuration parmaeters.\n\n| Name | Environment Variable | Description | Required | Example | Default |\n| --- | --- | --- | --- | --- | --- |\n| `apiUrl` | `NR_CMDB_SNOW_PROVIDER_APIURL` | The ServiceNow ReST API URL | Y | https://my-service-now.service-now.com | |\n| `authType` | `NR_CMDB_SNOW_PROVIDER_AUTHTYPE` | The type of authentication to use to authenticate with the ServiceNow instance (`basic` or `oauth`) | Y | `basic` | `basic` |\n| `apiUser` | `NR_CMDB_SNOW_PROVIDER_APIUSER` | The ServiceNow username to use when using `basic` authentication | Y if `authType` is `basic` | `admin` | |\n| `apiPassword` | `NR_CMDB_SNOW_PROVIDER_APIPASSWORD` | The password to use for the specified ServiceNow username when using `basic` authentication | Y if `authType` is `basic` | `abcd123` | |\n| `oauthTokenUrl` | `NR_CMDB_SNOW_PROVIDER_OAUTHTOKENURL` | The token URL to use when using `oauth` authentication | N | `https://myco.apis.com/auth` | `${apiUrl}/oauth_token.do` |\n| `oauthGrantType` | `NR_CMDB_SNOW_PROVIDER_GRANTTYPE` | The grant type to use when using `oauth` authentication | N | `client_credentials` | `password` |\n| `oauthClientId` | `NR_CMDB_SNOW_PROVIDER_OAUTHCLIENTID` | The client ID to use when using `oauth` authentication | Y if `authType` is `oauth` | `12345` | |\n| `oauthClientSecret` | `NR_CMDB_SNOW_PROVIDER_OAUTHCLIENTSECRET` | The client secret to use when using `oauth` authentication | Y if `authType` is `oauth` | `12345` | |\n| `oauthClientScopes` | `NR_CMDB_SNOW_PROVIDER_OAUTHCLIENTSCOPES` | The list of OAuth scopes to request when using `oauth` authentication. Separate multiple scopes using whitespace characters. | N | `read_profile` | |\n| `pageSize` | `NR_CMDB_SNOW_PROVIDER_PAGESIZE` | A New Relic User API key | N | `10` | `10000` |\n\n#### Mapping parameters\n\nThe `mappings` section of the configuration file is used to specify one or more\nmapping configurations. Each mapping configuration specifies a set of\nconfiguration parameters that defines the set of criteria for selecting external\nentities, the set of criteria for selecting New Relic entities, the criteria\nused to match external entities to New Relic entities, and the mapping from\nexternal entity key-values to New Relic entity tags.\n\n##### External entity query criteria\n\nThe `extEntityQuery` section of a mapping configuration specifies the query\ncriteria for selecting a set of external entities. The configuration parameters\nin this section are specific to the provider.\n\n###### ServiceNow CMDB entity query criteria\n\nThe ServiceNow CMDB provider supports the following configuration parameters for\nselecting the set of CIs that are candidates for matching against New Relic\nentities.\n\n| Name | Description | Required | Example | Default |\n| --- | --- | --- | --- | --- |\n| `type` | The ServiceNow CMDB configuration item type name | Y | `cmdb_ci_email_server` | |\n| `query` | An [encoded query string](https://docs.servicenow.com/csh?topicname=c_EncodedQueryStrings\u0026version=utah\u0026pubname=utah-platform-user-interface) to use to filter the result using  the `sysparm_query` parameter | N | `sys_updated_on\u003ejavascript:gs.dateGenerate('{{ .lastUpdateDate }}','{{ .lastUpdateTime }}')^operational_status!=2` | |\n| `serverTimezone` | A [location name](https://pkg.go.dev/time#LoadLocation) corresponding to a file in the IANA Time Zone database for the time zone of the local ServiceNow instance | N | `America/New_York` | |\n| `urlQueryParams` | Additional URL query parameters to send on the ReST `table` API call specified as a set of key + value pairs | N | (see below) | |\n\nThe ServiceNow CMDB query is executed by querying the `table` API using a URL\nlike the following.\n\n`https://my-service-now.service-now.com/my/api/now/table/CI_TYPE?sysparm_fields=sys_id,FIELD1,...,FIELDN\u0026sysparm_limit=PAGESIZE\u0026sysparm_offset=0`\n\nThe value of `CI_TYPE` is the value of the `type` configuration parameter. The\nvalues of `FIELD1,...,FIELDN` are the keys of the `mapping` node as well as the\nvalue of the `extEntityKey` in the `match` node. The value of `PAGESIZE` is the\nvalue of the `pageSize` parameter of the `provider` node.\n\n**Query parameter**\n\nIf a `query` is specified in the entity query criteria, the `sysparm_query`\nquery parameter will be added to the query portion of the URL. The `query`\nparameter value will be _automatically_ URL encoded so it should not be\nspecified in URL encoded format. For example to filter records where the\n`active` field is `true` and the `roles` field is `itil`, specify the string\n`active=true^roles=itil` and not `active%3Dtrue%5Eroles%3Ditil`.\n\nWhen using [delta synchronization](#delta-synchronization), the special\ncharacter sequences `${lastUpdateDate}` and `${lastUpdateTime}` will be replaced\nwith the date and time strings, respectively, for the date and time specified by\nthe last synchronization timestamp. The date and time strings will be in the\nformat required by the [dateGenerate](https://docs.servicenow.com/bundle/utah-api-reference/page/app-store/dev_portal/API_reference/glideSystemScoped/concept/c_GlideSystemScopedAPI.html#title_r_SGSYS-dateGenerate_S_S)\nfunction and will be converted to strings using the timezone specified in the\n`serverTimezone` parameter.\n\nThe `query` configuration parameter should not be confused with the\n`urlQueryParameters` configuration parameter. The former is used to specify an\n[encoded query string](https://docs.servicenow.com/csh?topicname=c_EncodedQueryStrings\u0026version=utah\u0026pubname=utah-platform-user-interface)\nused to filter the ServiceNow query result set. The latter is used to specify\nadditional query parameters to add to the ReST API URL generated to execute the\n`table` API query.\n\n**Example**\n\nConsider the following YAML.\n\n```yaml\n...\nprovider:\n  type: servicenow\n  pageSize: 50\nmappings:\n- extEntityQuery:\n    type: cmdb_ci_email_server\n    query: 'sys_updated_on\u003ejavascript:gs.dateGenerate('${lastUpdateDate}','${lastUpdateTime}')^operational_status!=2'\n    serverTimezone: America/Los_Angeles\n    urlQueryParams:\n      sysparm_display_value: \"true\"\n  entityQuery:\n    ...\n  match:\n    extEntityKey: name\n    ...\n  mapping:\n    sys_class_name: foo\n    environment: bar\n```\n\nGiven this YAML, the ServiceNow CMDB provider will access the URL\n`https://my-service-now.service-now.com/my/api/now/table/cmdb_ci_email_server?sysparm_fields=sys_id,name,sys_class_name,environment\u0026sysparm_limit=50\u0026sysparm_offset=0\u0026sysparm_display_value=true\u0026sysparm_query=sys_updated_on%3Ejavascript%3Ags.dateGenerate%28%272023-06-01%27%2C%2712%3A00%3A00%27%29%5Eoperational_status%21%3D2`\nto retrieve the *display values* for the fields `sys_id`, `name`,\n`sys_class_name`, and `environment` for the CI records of type\n`cmdb_ci_email_server` that were updated on or after\nJune 1st, 2023 at 12:00:00 GMT-7 and do not have an `operational_status` of `2`.\n\n##### New Relic entity query criteria\n\nThe `entityQuery` section of a mapping configuration specifies the query\ncriteria for selecting a set of entities in New Relic.\n\n| Name | Description | Required | Example | Default |\n| --- | --- | --- | --- | --- |\n| `type` | The entity type (`APPLICATION`, `HOST`, etc) | N | `WORKLOAD` | |\n| `domain` | The entity domain (`APM`, `BROWSER`, etc) | N | `INFRA` | |\n| `name` | The entity name | N | `Billing Service` | |\n| `accountId` | The New Relic account ID | N | 123456 | |\n| `tags` | A set of tag key + values pairs | N | (see below) | |\n| `query` | A raw `entitySearch` query | N | `type IN ('APPLICATION')` | |\n\nThe `tags` value is an array of key and values pairs as in the following\nexample.\n\n```yaml\nmappings:\n- entityQuery:\n  ...\n  tags:\n    foo:\n    - bar\n    - baz\n  ...\n```\n\nNote that the value part of the pair is an _array_ since New Relic tags can have\nmultiple values per key.\n\nThe query is executed via the Nerdgraph GraphQL API using the `entitySearch`\ntype of the `actor` type as follows.\n\n- If a value is specified for the `query` key, it will take precedence over the\n  other values.\n- Otherwise, the `entitySearch` query value will be built by `AND`'ing the other\n  values together. The following examples show the query string that would be\n  produced for a given `entityQuery` configuration.\n\n**Example 1: YAML**\n\n```yaml\nmappings:\n- entityQuery:\n  type: 'HOST'\n  domain: 'INFRA'\n  name: 'myinstance'\n```\n\n**Example 1: Query**\n\n`type IN ('HOST') AND domain IN ('INFRA') AND name LIKE 'myinstance'`\n\n**Example 2: YAML**\n\n```yaml\nmappings:\n- entityQuery:\n  accountId: 12345\n  tags:\n    foo:\n    - bar\n    beep:\n    - boop\n```\n\n**Example 2: Query**\n\n`tags.accountId = 12345 AND tags.foo IN ('bar') AND tags.beep IN ('boop')`\n\n**Example 3: YAML**\n\n```yaml\nmappings:\n- entityQuery:\n  type: 'HOST'\n  domain: 'INFRA'\n  query: \"name LIKE 'abc123'\"\n```\n\n**Example 3: Query**\n\n`name LIKE 'abc123'`\n\n##### Match strategy\n\nThe `match` section of a mapping configuration is a tuplet that specifies the\nname of an external entity key, the name of a New Relic entity attribute/tag,\nand an operator used to compare the values of the external entity key to\nthe New Relic entity attribute/tag.\n\n| Name | Description | Required | Example | Default |\n| --- | --- | --- | --- | --- |\n| `extEntityKey` | The external entity key to use for comparison | Y | `environment` | |\n| `operator` | The type of comparison to use | Y | `equal` | |\n| `entityKey` | The New Relic entity attribute/tag to use for comparison  | Y | `equal` | |\n\nThe `extEntityKey` external entity key will be implicitly added to the list of\nkeys requested from the provider for each external entity , regardless of whether\nthe key is referenced in the [mapping](#mapping) section.\n\nThe New Relic entity attribute/tag may be any one of the following.\n\n* `name` - the New Relic entity name\n* `guid` - the New Relic entity GUID\n* `accountId` - the New Relic account ID that the entity belongs to\n* Any tag name\n\nThe following values for the `operator` attribute are supported.\n\n| Value | Meaning |\n| --- | --- |\n| `equal` | external entity key value is case-sensitive equivalent to New Relic entity attribute/tag value |\n| `equal-ignore-case` | external entity key value is case-insensitive equivalent to New Relic entity attribute/tag value |\n| `contains` | New Relic entity attribute/tag value is a case-sensitive sub-string within external entity key value |\n| `contains-ignore-case` | New Relic entity attribute/tag value is a case-insensitive sub-string within external entity key value |\n| `inverse-contains-ignore-case` | external entity key value is a case-insensitive sub-string within New Relic entity attribute/tag value |\n\nFor example, consider the following YAML.\n\n```yaml\nmatch:\n  extEntityKey: foo\n  operator: equal-ignore-case\n  entityKey: bar\n```\n\nGiven this YAML, New Relic entities will be matched against external entities by\ncomparing the values of the tag `bar` on the New Relic entities to the values of\nthe key `foo` on the external entities, case insensitively.\n\n##### Mapping\n\nThe `mapping` node of a mapping configuration specifies the mapping from\nexternal entity keys to New Relic entity tags. The keys of the `mapping` node\nrepresent the external entity keys while the values represent the New Relic\nentity tag names.\n\nFor a given external entity to New Relic entity pair, the value of each of the\nexternal entity keys specified by the keys in the `mapping` node are used as the\nvalues of the New Relic entity tags specified by the values in the `mapping`\nnode.\n\nFor example, consider the following YAML.\n\n```yaml\nmapping:\n  foo: bar\n  beep: boop\n```\n\nGiven this YAML, the values of the external entity key `foo` and `beep` of any\nexternal entity that matches a New Relic entity will be set as the values of the\ntags `bar` and `boop` on the matching New Relic entity.\n\n#### Full example\n\nThis section provides an example configuration and set of entities followed by\na full walk-through of the synchronization process.\n\n##### Sample configuration\n\n```yaml\napiUrl: https://api.newrelic.com\nlog:\n  level: warn\nprovider:\n  type: servicenow\n  apiUrl: https://my-service-now.service-now.com\n  apiUser: admin\nmappings:\n- extEntityQuery:\n    type: cmdb_ci_email_server\n  entityQuery:\n    type:\n    - APPLICATION\n    domain:\n    - APM\n    accountId: 1\n  match:\n    extEntityKey: name\n    operator: equal-ignore-case\n    entityKey: name\n  mapping:\n    sys_class_name: SNOW_CI_CLASS\n    sys_id: SNOW_CMDB_CI\n    environment: SNOW_ENVIRONMENT\n    sys_domain.value: SNOW_SYS_DOMAIN\n- extEntityQuery:\n    type: cmdb_ci_app_server\n  entityQuery:\n    type:\n    - HOST\n    domain:\n    - INFRA\n  match:\n    extEntityKey: name\n    operator: contains\n    entityKey: ciMatch\n  mapping:\n    sys_class_name: SNOW_CI_CLASS\n    sys_id: SNOW_CMDB_CI\n    environment: SNOW_ENVIRONMENT\n    sys_domain.value: SNOW_SYS_DOMAIN\n```\n\n##### ServiceNow CIs\n\n| Type | Name | sys_id | sys_class_name | sys_domain.value | environment |\n| --- | --- | --- | --- | --- | --- |\n| cmdb_ci_email_server | Microsoft Exchange | abcd123 | cmdb_ci_email_server | global | Production |\n| cmdb_ci_app_server | WebSphere Application Server | efgh456 | cmdb_ci_app_server | local | Development |\n\n##### New Relic Entities\n\n| Type | Domain | Name | GUID | tags.ciMatch | tags.SNOW_CI_CLASS | tags.SNOW_CMDB_CI | tags.SNOW_SYS_DOMAIN | tags.SNOW_ENVIRONMENT |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| APPLICATION | APM | microsoft exchange | NR12345 | | cmdb_ci_email_server | abcd123 |  | Development |\n| HOST | INFRA | WebSphere Application Server | NR45678 | WebSphere | cmdb_ci_app_server | efgh456 | global |  |\n\n##### Walkthrough\n\n1. The entity tag sync application starts up and reads in the configuration.\n1. A new ServiceNow provider is created with the API base URL\n  `https://my-service-now.service-now.com` and the API username `admin`. The API\n   password will be read from the environment variable\n   `NR_CMDB_SNOW_PROVIDER_APIPASSWORD`. The page size defaults to 10000.\n1. The application starts processing the first mapping configuration by\n   inspecting the `extEntityQuery` node.\n1. Using the `type` parameter specified in the `extEntityQuery` node as the\n   table name to query and using the keys of the `mapping` node as well as the\n   value of the `extEntityKey` of the `match` node as the CI fields to return,\n   the ServiceNow CMDB provider will make an HTTP `GET` request for the\n   following URL.\n\n   `https://my-service-now.service-now.com/my/api/now/table/cmdb_ci_email_server?sysparm_fields=sys_id,name,sys_class_name,environment,sys_domain\u0026sysparm_limit=10000\u0026sysparm_offset=0`\n\n   This API call will return the requested key-value pairs for all CIs of type\n   `cmdb_ci_email_server`, including the CI listed above with `sys_id`\n   `abcd123`.\n1. The application next uses the `entityQuery` node to request to make the\n   following GraphQL query against the New Relic GraphQL API.\n\n   ```graphql\n   {\n     actor {\n       entitySearch(query: \"type IN ('APPLICATION') AND domain IN ('APM') AND tags.accountId` = 1\") {\n         count\n         results {\n           entities {\n             guid\n             name\n             accountId\n             domain\n             type\n             alertSeverity\n             permalink\n             reporting\n             tags {\n               key\n               values\n             }\n           }\n         }\n       }\n     }\n   }\n   ```\n\n   This query will return all APM service entities in account ID 1, including\n   the APM application listed above with GUID `NR12345`.\n1. Next, the application will iterate over the entities returned from the\n   GraphQL call and, per the `match` node values, will perform a\n   case-insensitive equals check between each CI name and the current entity\n   name. In this case, the application will find a match between the CI with\n   `sys_id` `abcd123` and the New Relic entity with GUID `NR12345` because the\n   values `Microsoft Exchange` and `microsoft exchange` are case-insensitively\n   equivalent.\n1. The application will update the tags on entity `NR12345` using the values of\n   the key-value pairs from CI `abcd123` as follows.\n\n   | CI Key | CI Value | Entity Tag | Entity `NR12345` before | Entity `NR12345` after|\n   | --- | --- | --- | --- | --- |\n   | sys_class_name | cmdb_ci_email_server | SNOW_CI_CLASS | cmdb_ci_email_server | cmdb_ci_email_server |\n   | sys_id | abcd123 | SNOW_CMDB_CI | abcd123 | abcd123 |\n   | sys_domain.value | global | SNOW_SYS_DOMAIN | | global |\n   | environment | Production | SNOW_ENVIRONMENT | Development | Production |\n1. The application starts processing the second mapping configuration by\n   inspecting the `extEntityQuery` node.\n1. Using the specified `type` parameter and the keys of the `mapping` node as\n   well as the value of the `extEntityKey` of the `match` node specified by the\n   configuration, the ServiceNow CMDB provider will make an HTTP `GET` request\n   for the following URL.\n\n   `https://my-service-now.service-now.com/my/api/now/table/cmdb_ci_app_server?sysparm_fields=sys_id,name,sys_class_name,environment,sys_domain\u0026sysparm_limit=10000\u0026sysparm_offset=0`\n\n   This API call will return the requested key-value pairs for all CIs of type\n   `cmdb_ci_app_server`, including the CI listed above with `sys_id`\n   `efgh456`.\n1. The application next uses the `entityQuery` node to request to make the\n   following GraphQL query against the New Relic GraphQL API.\n\n   ```graphql\n   {\n     actor {\n       entitySearch(query: \"type IN ('HOST') AND domain IN ('INFRA')\") {\n         count\n         results {\n           entities {\n             guid\n             name\n             accountId\n             domain\n             type\n             alertSeverity\n             permalink\n             reporting\n             tags {\n               key\n               values\n             }\n           }\n         }\n       }\n     }\n   }\n   ```\n   This query will return all infrastructure host entities accessible by the\n   given license key, including the infrastructure host listed above with GUID\n   `NR45678`.\n1. Next, the application will iterate over the entities returned from the\n   GraphQL call and, per the `match` node values, will perform a\n   case-insensitive \"contains\" check between each CI name and the _first_ value\n   of the `ciMatch` tag of each entity. In this case, the application will find\n   a match between the CI with `sys_id` `efgh456` and the New Relic entity with\n   GUID `NR45678` because the value `WebSphere Application Server` includes\n   the value `WebSphere`.\n1. The application will update the tags on entity `NR45678` using the values of\n   the key-value pairs from CI `efgh456` as follows.\n\n   | CI Key | CI Value | Entity Tag | Entity `NR45678` before | Entity `NR45678` after|\n   | --- | --- | --- | --- | --- |\n   | sys_class_name | cmdb_ci_app_server | SNOW_CI_CLASS | cmdb_ci_email_server | cmdb_ci_email_server |\n   | sys_id | efgh456 | SNOW_CMDB_CI | efgh456 | efgh456 |\n   | sys_domain.value | local | SNOW_SYS_DOMAIN | global | local |\n   | environment | Production | SNOW_ENVIRONMENT | | Development |\n\n## Building\n\n### Coding Conventions\n\n#### Style Guidelines\n\nWhile not strictly enforced, the basic preferred editor settings are set in the\n[.editorconfig](./.editorconfig). Other than this, no style guidelines are\ncurrently imposed.\n\n#### Static Analysis\n\nThis project uses both [`go vet`](https://pkg.go.dev/cmd/vet) and\n[`staticcheck`](https://staticcheck.io/) to perform static code analysis. These\nchecks are run via [`precommit`](https://pre-commit.com) on all commits. Though\nthis can be bypassed on local commit, both tasks are also run during\n[the `validate` workflow](./.github/workflows/validate.yml) and must have no\nerrors in order to be merged.\n\n#### Commit Messages\n\nCommit messages must follow [the conventional commit format](https://www.conventionalcommits.org/en/v1.0.0/).\nAgain, while this can be bypassed on local commit, it is strictly enforced in\n[the `validate` workflow](./.github/workflows/validate.yml).\n\nThe basic commit message structure is as follows.\n\n```\n\u003ctype\u003e[optional scope][!]: \u003cdescription\u003e\n\n[optional body]\n\n[optional footer(s)]\n```\n\nIn addition to providing consistency, the commit message is used by\n[svu](https://github.com/caarlos0/svu) during\n[the release workflow](./.github/workflows/release.yml). The presence and values\nof certain elements within the commit message affect auto-versioning. For\nexample, the `feat` type will bump the minor version. Therefore, it is important\nto use the guidelines below and carefully consider the content of the commit\nmessage.\n\nPlease use one of the types below.\n\n- `feat` (bumps minor version)\n- `fix` (bumps patch version)\n- `chore`\n- `build`\n- `docs`\n- `test`\n\nPlease use one of the scopes below.\n\n- `sync` - work related to the synchronization process\n- `config` - work related to the config system\n- `provider[-optional-name]` - work related to the provider framework or a\n  specific provider\n- `lambda` - work related to the lambda code and/or deployment\n- `ci` - work related to continuous integration (GitHub workflow/actions)\n- `release` - work related to creating a new release\n\nAny type/scope can be followed by the `!` character to indicate a breaking\nchange. Additionally, any commit that has the text `BREAKING CHANGE:` in the\nfooter will indicate a breaking change.\n\n### Local Development\n\nFor local development, simply use `go build` and `go run`. For example,\n\n```bash\ngo build cmd/nr-entity-tag-sync/nr-entity-tag-sync.go\n```\n\nOr\n\n```bash\ngo run cmd/nr-entity-tag-sync/nr-entity-tag-sync.go\n```\n\nIf you prefer, you can also use [`goreleaser`](https://goreleaser.com/) with\nthe `--single-target` option to build the binary for the local `GOOS` and\n`GOARCH` only.\n\n```bash\ngoreleaser build --single-target\n```\n\n### Releases\n\nReleases are built and packaged using [`goreleaser`](https://goreleaser.com/).\nBy default, a new release will be built automatically on any push to the `main`\nbranch. For more details, review the [`.goreleaser.yaml`](./.goreleaser.yaml)\nand [the `goreleaser` documentation](https://goreleaser.com/intro/).\n\nThe [svu](https://github.com/caarlos0/svu) utility is used to generate the next\ntag value [based on commit messages](https://github.com/caarlos0/svu#commit-messages-vs-what-they-do).\n\n### GitHub Workflows\n\nThis project utilizes GitHub workflows to perform actions in response to\ncertain GitHub events.\n\n| Workflow | Events | Description\n| --- | --- | --- |\n| [validate](./.github/workflows/validate.yml) | `push` | Runs [precommit](https://pre-commit.com) to perform static analysis and runs [commitlint](https://commitlint.js.org/#/) to validate the last commit message |\n| [build](./.github/workflows/build.yml) | `pull_request` | Builds and tests code |\n| [release](./.github/workflows/release.yml) | `push` to `main` branch | Generates a new tag using [svu](https://github.com/caarlos0/svu) and runs [`goreleaser`](https://goreleaser.com/) |\n| [repolinter](./.github/workflows/repolinter.yml) | `pull_request` | Enforces repository content guidelines |\n\n## Testing\n\nTBD\n\n## Support\n\nNew Relic has open-sourced this project. This project is provided AS-IS WITHOUT\nWARRANTY OR DEDICATED SUPPORT. Issues and contributions should be reported to\nthe project here on GitHub.\n\nWe encourage you to bring your experiences and questions to the\n[Explorers Hub](https://discuss.newrelic.com/) where our community members\ncollaborate on solutions and new ideas.\n\n### Privacy\n\nAt New Relic we take your privacy and the security of your information\nseriously, and are committed to protecting your information. We must emphasize\nthe importance of not sharing personal data in public forums, and ask all users\nto scrub logs and diagnostic information for sensitive information, whether\npersonal, proprietary, or otherwise.\n\nWe define “Personal Data” as any information relating to an identified or\nidentifiable individual, including, for example, your name, phone number, post\ncode or zip code, Device ID, IP address, and email address.\n\nFor more information, review [New Relic’s General Data Privacy Notice](https://newrelic.com/termsandconditions/privacy).\n\n### Contribute\n\nWe encourage your contributions to improve this project! Keep in mind that\nwhen you submit your pull request, you'll need to sign the CLA via the\nclick-through using CLA-Assistant. You only have to sign the CLA one time per\nproject.\n\nIf you have any questions, or to execute our corporate CLA (which is required\nif your contribution is on behalf of a company), drop us an email at\nopensource@newrelic.com.\n\n**A note about vulnerabilities**\n\nAs noted in our [security policy](../../security/policy), New Relic is committed\nto the privacy and security of our customers and their data. We believe that\nproviding coordinated disclosure by security researchers and engaging with the\nsecurity community are important means to achieve our security goals.\n\nIf you believe you have found a security vulnerability in this project or any of\nNew Relic's products or websites, we welcome and greatly appreciate you\nreporting it to New Relic through [HackerOne](https://hackerone.com/newrelic).\n\nIf you would like to contribute to this project, review [these guidelines](./CONTRIBUTING.md).\n\nTo all contributors, we thank you!  Without your contribution, this project\nwould not be what it is today.\n\n### License\n\nThe [New Relic Entity Tag Sync] project is licensed under the\n[Apache 2.0](http://apache.org/licenses/LICENSE-2.0.txt) License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnewrelic%2Fnr-entity-tag-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnewrelic%2Fnr-entity-tag-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnewrelic%2Fnr-entity-tag-sync/lists"}