{"id":81849,"date":"2018-11-29T14:52:04","date_gmt":"2018-11-29T14:52:04","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=81849"},"modified":"2026-04-15T19:14:07","modified_gmt":"2026-04-15T19:14:07","slug":"power-bi-introduction-visualizing-sql-server-audit-data-part-9","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/power-bi-introduction-visualizing-sql-server-audit-data-part-9\/","title":{"rendered":"Visualize SQL Server Audit Data in Power BI: Tables, Charts, and Gauges"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"h-executive-summary\">Executive Summary<\/h2>\n\n\n\n<p><strong>SQL Server Audit generates detailed logs of database activity &#8211; DML statements (INSERT, UPDATE, DELETE), DDL changes (CREATE TABLE, ALTER TABLE), login events, and more. Power BI Desktop can connect to these audit views and transform raw audit data into interactive reports with filters, charts, and gauges. This article demonstrates the complete workflow: setting up a test ImportSales database with a Customers table, generating audit data with DML and DDL operations, connecting Power BI to the SQL Server audit views, and building a multi-visual report with a table, matrix, bar chart, and gauge to monitor and analyse the audit log. Part 9 of the Power BI Introduction series.<\/strong><\/p>\n\n\n<p><strong>The series so far:<\/strong><\/p>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-part-1-introduction\/\">Power BI Introduction: Tour of Power BI \u2014 Part 1<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-working-with-power-bi-desktop-part-2\/\">Power BI Introduction: Working with Power BI Desktop \u2014 Part 2<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-working-with-r-scripts-in-power-bi-desktop-part-3\/\">Power BI Introduction: Working with R Scripts in Power BI Desktop \u2014 Part 3<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-working-with-parameters-in-power-bi-desktop-part-4\/\">Power BI Introduction: Working with Parameters in Power BI Desktop \u2014 Part 4<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-working-with-sql-server-data-in-power-bi-desktop-part-5\/\">Power BI Introduction:\u00a0Working with SQL Server data in Power BI Desktop \u2014 Part 5<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/homepage\/power-bi-introduction-power-query-m-formula-language-in-power-bi-desktop-part-6\/\">Power BI Introduction:\u00a0Power Query M Formula Language in Power BI Desktop \u2014 Part 6<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-building-reports-in-power-bi-desktop-part-7\/\">Power BI Introduction: Building Reports in Power BI Desktop \u2014 Part 7<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-publishing-reports-to-the-power-bi-service-part-8\/\">Power BI Introduction: Publishing Reports to the Power BI Service \u2014 Part 8<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-visualizing-sql-server-audit-data-part-9\/\">Power BI Introduction: Visualizing SQL Server Audit Data \u2014 Part 9<\/a><\/li>\n<\/ol>\n\n\n\n\n<p>Database teams trying to comply with regulations such as the Health Insurance Portability and Accountability Act of 1996 (HIPAA) or the Sarbanes-Oxley Act of 2002 (SOX) commonly use auditing as part of their compliance strategies to help track potential threats to stored data. For example, teams running SQL Server might turn to SQL Server Audit to log actions at both the server and database levels. SQL Server Audit is built into the database engine and, starting with SQL Server 2016, is available in all SQL Server editions.<\/p>\n\n\n\n<p>If you\u2019re a DBA tasked with implementing SQL Server Audit, you\u2019ll find that setting up the auditing components is a relatively straightforward process. The more significant challenges often lie in trying to determine what user actions to audit, how to handle the potentially large amounts of audit data, and what tools to use to monitor and review that data.<\/p>\n\n\n\n<p>Although these are all important considerations, this article is concerned primarily with the last one\u2014monitoring and reviewing the audit data. SQL Server makes it easy to collect the data but provides no mechanisms for working with that data in a meaningful way, other than to review it manually.<\/p>\n\n\n\n<p>One way to address this issue is to bring in a third-party solution such as <a href=\"https:\/\/www.eventtracker.com\/\">EventTracker<\/a> to consolidate, manage, and monitor the audit data, but this comes with additional licensing and implementation considerations.<\/p>\n\n\n\n<p>Another approach is to use Power BI to create reports that provide quick visual insights into the data. Although Power BI is not designed for auditing and alerting to the same degree as more robust log management tools, it provides a relatively simple method for tracking user behavior. Best of all, the basic Power BI service is free, as is Power BI Desktop, a downloadable tool for transforming and visualizing different types of data.<\/p>\n\n\n\n<p>In this article, I demonstrate how you can use Power BI to work with SQL Server Audit data, walking you through the steps necessary to set up a test environment, generate sample audit data, pull that data into Power BI Desktop, and create a report that contains tables and visualizations. Keep in mind, however, that SQL Server Audit and Power BI Desktop are both powerful solutions that support far more capabilities than can be covered in a single article. The information here should at least provide you with a conceptual overview of how the tools can be used together and what it takes to get started using Power BI Desktop to review audit data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-setting-up-a-test-environment\">Setting Up a Test Environment<\/h2>\n\n\n\n<p>To prepare your environment for the examples in this article, you should first create the <code>ImportSales<\/code> test database, which contains one schema, <code>Sales<\/code>, and one table, <code>Sales.Customers<\/code>. You can then populate the table with data from the <code>Sales.Customers<\/code> table in the <code>WideWorldImporters<\/code> database. For this article, all audited actions are limited to <code>Customers<\/code> table in the <code>ImportSales<\/code> database.<\/p>\n\n\n\n<p>To create the <code>ImportSales<\/code> database, run the following T-SQL code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">USE master;\nGO\nDROP DATABASE IF EXISTS ImportSales;\nGO\nCREATE DATABASE ImportSales;\nGO\nUSE ImportSales;\nGO\nCREATE SCHEMA Sales;\nGO\nCREATE TABLE Sales.Customers(\n  CustID INT IDENTITY PRIMARY KEY,\n  Customer NVARCHAR(100) NOT NULL,\n  Contact NVARCHAR(50) NOT NULL,\n  Email NVARCHAR(256) NULL,\n  Phone NVARCHAR(20) NULL,\n  Category NVARCHAR(50) NOT NULL);\nGO\nINSERT INTO Sales.Customers(Customer, Contact, Email, Phone, Category) \nSELECT c.CustomerName, p.FullName, p.EmailAddress,\n  p.PhoneNumber, cc.CustomerCategoryName\nFROM WideWorldImporters.Sales.Customers c\n  INNER JOIN WideWorldImporters.Application.People p\n    ON c.PrimaryContactPersonID = p.PersonID\n  INNER JOIN WideWorldImporters.Sales.CustomerCategories cc\n    ON c.CustomerCategoryID = cc.CustomerCategoryID;\nGO<\/pre>\n\n\n\n<p>If you don\u2019t have the <code>WideWorldImporters<\/code> database installed, you can populate the <code>Customers<\/code> table with your own data. If you want to use a different table and database for these examples, skip the T-SQL statements above and use a database and table that best suit your needs (and are not in a production environment, of course). Just be sure to replace any references to the <code>ImportSales<\/code> database or <code>Customers<\/code> table in the subsequent examples.<\/p>\n\n\n\n<p>The next step is to create an audit object at the SQL Server instance level and a database audit specification at the <code>ImportSales<\/code> database level. The audit object serves as a container for organizing the server and database audit settings and for delivering the final audit logs. For this article, you\u2019ll be saving the audit data to a local folder, but know that SQL Server Audit also lets you save the data to the Windows Application log or Security log.<\/p>\n\n\n\n<p>A database audit specification is created at the database level and is associated with an audit object, which means that the audit object needs to exist before you can create the database audit specification. The specification determines what actions should be audited at the database level. You can also create a server audit specification for auditing actions at the server level, but for this article, you need only the database audit specification.<\/p>\n\n\n\n<p>To create both the audit object and database audit specification, run the following T-SQL code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">USE master;  \nGO  \nCREATE SERVER AUDIT ImportSalesAudit  \nTO FILE (FILEPATH = 'C:\\DataFiles\\audit\\');  \nGO  \nALTER SERVER AUDIT ImportSalesAudit  \nWITH (STATE = ON);  \nGO  \nUSE ImportSales;  \nGO  \nCREATE DATABASE AUDIT SPECIFICATION ImportSalesDbSpec \nFOR SERVER AUDIT ImportSalesAudit\nADD (SCHEMA_OBJECT_CHANGE_GROUP),\nADD (SELECT, INSERT, UPDATE, DELETE \n  ON Object::Sales.Customers BY public)  \nWITH (STATE = ON);  \nGO<\/pre>\n\n\n\n<p>The <code>CREATE<\/code> <code>SERVER<\/code> <code>AUDIT<\/code> statement creates an audit object named <code>ImportSalesAudit<\/code> that saves the audit data to the <em>C:\\DataFiles\\Audit<\/em> folder. You must then run an <code>ALTER<\/code> <code>SERVER<\/code> <code>AUDIT<\/code> statement as a separate step to set the <code>STATE<\/code> property to <code>ON<\/code>.<\/p>\n\n\n\n<p>Next comes the <code>CREATE<\/code> <code>DATABASE<\/code> <code>AUDIT<\/code> <code>SPECIFICATION<\/code> statement, which defines a specification named <code>ImportSalesDbSpec<\/code>. The specification includes two <code>ADD<\/code> clauses. The first <code>ADD<\/code> clause specifies the action group <code>SCHEMA_OBJECT_CHANGE_GROUP<\/code>, which audits all <code>CREATE<\/code>, <code>ALTER<\/code>, and <code>DROP<\/code> statements issued against any schema objects in the database. Because this is a group action, it must be specified separately from individual actions, such as those in the second <code>ADD<\/code> clause.<\/p>\n\n\n\n<p>The second <code>ADD<\/code> clause specifies four individual actions:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>The <code>SELECT<\/code> action audits all <code>SELECT<\/code> statements.<\/li>\n\n\n\n<li>The <code>INSERT<\/code> action audits all <code>INSERT<\/code> statements.<\/li>\n\n\n\n<li>The <code>UPDATE<\/code> action audits all <code>UPDATE<\/code> statements.<\/li>\n\n\n\n<li>The <code>DELETE<\/code> action audits all <code>DELETE<\/code> statements.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>The <code>ON<\/code> subclause in the second <code>ADD<\/code> clause points to the <code>Customers<\/code> table, which means that the <code>SELECT<\/code>, <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> actions are all specific to that table. In addition, because the <code>BY<\/code> subclause specifies the <code>public<\/code> login, the auditing applies to all users.<\/p>\n\n\n\n<p>Under normal circumstances, you would likely be auditing many more users and actions than you\u2019ve done here, but this is enough to demonstrate the basic principles behind using Power BI to review audit data.<\/p>\n\n\n\n<p>With the auditing structure in place, run the following T-SQL code to create three test user accounts within the <code>ImportSales<\/code> database, assigning a different set of permissions to each user:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">CREATE USER User01 WITHOUT LOGIN;\nGRANT ALTER, SELECT, INSERT, DELETE, UPDATE\nON OBJECT::Sales.Customers TO user01;  \nGO\nCREATE USER User02 WITHOUT LOGIN;\nGRANT SELECT, INSERT, DELETE, UPDATE\nON OBJECT::Sales.Customers TO user02;  \nGO\nCREATE USER User03 WITHOUT LOGIN;\nGRANT SELECT\nON OBJECT::Sales.Customers TO user03;  \nGO<\/pre>\n\n\n\n<p>The user accounts are created without logins to keep the testing simple. For the same reason, all the granted permissions are specific to the <code>Customers<\/code> table, with access defined as follows:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>User01<\/code> can access and modify all data within the table as well as update the table\u2019s definition.<\/li>\n\n\n\n<li><code>User02<\/code> can access and modify all data within the table but is not permitted to update the table\u2019s definition.<\/li>\n\n\n\n<li><code>User03<\/code> can access all data within the table but is not permitted to modify any of the data or update the table\u2019s definition.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>You can set up the test users and their permissions any way you like, so that you have them in place when you\u2019re ready to generate audit data to use in Power BI Desktop.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-generating-test-audit-data\">Generating Test Audit Data<\/h2>\n\n\n\n<p>To generate the audit data, you should run a series of data manipulation language (DML) statements and data definition language (DDL) statements against the <code>Customers<\/code> table, running them within the execution context of the three database user accounts. The easiest way to do this is to use an <code>EXECUTE<\/code> <code>AS<\/code> statement to specify the user context, run one or more statements, and then run a <code>REVERT<\/code> statement to switch back to the original user.<\/p>\n\n\n\n<p>You can run whatever DML and DDL statements you want, as long as you test each account and the permissions assigned to that account. On my system, I ran several sets of T-SQL statements, most of them multiple times to generate a reasonable amount of data. If you want to use the same statements that I used, start with the following DML statements, running them under the <code>User01<\/code> account:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXECUTE AS USER = 'User01';\nSELECT * FROM Sales.Customers;\nINSERT INTO Sales.Customers \n  (Customer, Contact, Email, Phone, Category)\n  VALUES('Wingtip Toys (Eugene, OR)', 'Flora Olofsson', \n    'flora@wingtiptoys.com', '(787) 555-0100', 'Gift Store');\nDECLARE @LastID INT = (SELECT SCOPE_IDENTITY())\nUPDATE Sales.Customers SET Category = 'Novelty Shop' \nWHERE CustID = @LastID;\nDELETE Sales.Customers WHERE CustID = @LastID;\nREVERT;\nGO<\/pre>\n\n\n\n<p>Be sure to run these statements multiple times, according to how much audit data you want available. I ran the DML statements about five times and the DDL statements either one or two times.<\/p>\n\n\n\n<p>After you run the preceding DML statements, run the following statements to add a column to the <code>Customers<\/code> table, again as <code>User01<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXECUTE AS USER = 'User01';\nALTER TABLE Sales.Customers\nADD Status BIT NOT NULL DEFAULT(1); \nREVERT;\nGO<\/pre>\n\n\n\n<p>Next, repeat the same DML statements for <code>User02<\/code>, again running the block of statements multiple times:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXECUTE AS USER = 'User02';\nSELECT * FROM Sales.Customers;\nINSERT INTO Sales.Customers \n  (Customer, Contact, Email, Phone, Category)\n  VALUES('Tailspin Toys (Bainbridge Island, WA)', 'Kanti Kotadia', \n    'kanti@tailspintoys.com', '(303) 555-0100', 'Gift Store');\nDECLARE @LastID INT = (SELECT SCOPE_IDENTITY())\nUPDATE Sales.Customers SET Category = 'Novelty Shop' \nWHERE CustID = @LastID;\nDELETE Sales.Customers WHERE CustID = @LastID;\nREVERT;\nGO<\/pre>\n\n\n\n<p>Now try to add another column to the <code>Customers<\/code> table, but this time as <code>User02<\/code>, using the following T-SQL:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXECUTE AS USER = 'User02';\nALTER TABLE Sales.Customers\nADD LastUpdated DATETIME NOT NULL DEFAULT(GETDATE()); \nREVERT;\nGO<\/pre>\n\n\n\n<p>The statement should generate an error because the <code>User02<\/code> account does not have the proper permissions to modify the table definition. Be aware, however, that if you run one or more T-SQL statements under the context of a specific user and one of the statements fails, the <code>REVERT<\/code> statement will not run, in which case, you must rerun the REVERT statement, without the other statements, to ensure that you close that execution context. (A better approach, of course, is to add the proper logic to your code to ensure that the <code>REVERT<\/code> statement always runs.)<\/p>\n\n\n\n<p>Next, run the same DML statements as before, but under the <code>User03<\/code> account:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXECUTE AS USER = 'User03';\nSELECT * FROM Sales.Customers;\nINSERT INTO Sales.Customers \n  (Customer, Contact, Email, Phone, Category)\n  VALUES('Tailspin Toys (Bainbridge Island, WA)', 'Kanti Kotadia', \n    'kanti@tailspintoys.com', '(303) 555-0100', 'Gift Store');\nDECLARE @LastID INT = (SELECT SCOPE_IDENTITY())\nUPDATE Sales.Customers SET Category = 'Novelty Shop' \nWHERE CustID = @LastID;\nDELETE Sales.Customers WHERE CustID = @LastID;\nREVERT;\nGO<\/pre>\n\n\n\n<p>In this case, the <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> statements each return an error because of the lack of permissions, which means you\u2019ll once again need to run the <code>REVERT<\/code> statement separately. The same goes for the following <code>ALTER<\/code> <code>TABLE<\/code> statement, which also returns an error:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXECUTE AS USER = 'User03';\nALTER TABLE Sales.Customers\nADD LastUpdated DATETIME NOT NULL DEFAULT(GETDATE()); \nREVERT;<\/pre>\n\n\n\n<p>Again, you can run whatever DML and DDL statements you like to generate the test audit statements. The idea is to have enough data to use in Power BI Desktop.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-connecting-to-sql-server-audit-data-in-power-bi-desktop\">Connecting to SQL Server Audit Data in Power BI Desktop<\/h2>\n\n\n\n<p>When connecting to SQL Server within Power BI Desktop, you can retrieve the data from specific tables and views, or you can run a query that returns exactly the data you need from multiple tables and views. With a query, you can also use system functions such as <code>sys.fn_get_audit_file<\/code>, a table-valued function that returns data from SQL Server Audit log files. (You cannot use this function to retrieve data from the Application or Security log.)<\/p>\n\n\n\n<p>For this article, you will use the function in the following <code>SELECT<\/code> statement to return the user account, action, success status, T-SQL statement, and event time of each logged event:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">SELECT f.database_principal_name [User Acct],\n  (CASE\n    WHEN a.name = 'STATEMENT ROLLBACK' THEN 'ROLLBACK'\n    ELSE a.name \n  END) [User Action], \n  (CASE\n    WHEN f.succeeded = 1 THEN 'Succeeded'\n    ELSE 'Failed'\n  END) [Succeeded],\n  f.statement [SQL Statement],\n  f.event_time [Date\/Time]\nFROM sys.fn_get_audit_file \n  ('C:\\DataFiles\\audit\\ImportSalesAudit_*.sqlaudit', \n   default, default) f\n  INNER JOIN (SELECT DISTINCT action_id, name \n  FROM sys.dm_audit_actions) a\n    ON f.action_id = a.action_id\nWHERE f.database_principal_name IN ('User01', 'User02', 'User03')<\/pre>\n\n\n\n<p>The statement joins the <code>sys.fn_get_audit_file<\/code> function to the <code>sys.dm_audit_actions<\/code> function to return an action\u2019s full name, rather than its abbreviation. The statement also limits the results to the three test user accounts.<\/p>\n\n\n\n<p>You\u2019ll use this <code>SELECT<\/code> statement when setting up your SQL Server connection in Power BI Desktop. To configure the connection, create a new report in Power BI Desktop, click the <em>Get Data<\/em> down arrow on the <em>Home<\/em> ribbon, and then click <em>SQL Server<\/em>. When the <em>SQL Server database<\/em> dialog box appears, click the <em>Advanced options<\/em> arrow to expand the dialog box, as shown in Figure 1.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"716\" height=\"679\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-199.png\" alt=\"\" class=\"wp-image-81851\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 1. Defining a SQL Server connection in Power BI Desktop<\/p>\n\n\n\n<p>To configure the connection:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li>Type the SQL Server instance in the <em>Server<\/em> text box.<\/li>\n\n\n\n<li>Type the name of the database, <code>ImportSales<\/code>, in the <em>Database<\/em> text box.<\/li>\n\n\n\n<li>Select an option in the <em>Data Connectivity<\/em> mode section. I chose <em>DirectQuery<\/em>.<\/li>\n\n\n\n<li>Type or paste the <code>SELECT<\/code> statement into the <em>SQL statement<\/em> text box.<\/li>\n<\/ol>\n<\/div>\n\n\n<p>When you select <em>DirectQuery,<\/em> no data is imported or copied into Power BI Desktop. Instead, Power BI Desktop queries the underlying data source whenever you create or interact with a visualization, ensuring that you\u2019re always viewing the most current data. Be aware, however, that if you plan to publish your report to the Power BI service, you\u2019ll need to set up a gateway connection that enables the service to retrieve the source data. Also, note that you cannot create a Power BI Desktop report that includes both a <em>DirectQuery<\/em> SQL Server connection and an <em>Import<\/em> SQL Server connection. You must choose one or the other.<\/p>\n\n\n\n<p>After you\u2019ve configured the connection in the <em>SQL Server<\/em> <em>database<\/em> dialog box, click <em>OK<\/em>. This launches a preview window, where you can view a sample of the audit data, as shown in Figure 2.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"893\" height=\"675\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-200.png\" alt=\"\" class=\"wp-image-81852\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 2. Verifying the SQL Server Audit data<\/p>\n\n\n\n<p>If everything looks as you would expect, click <em>Load<\/em> to make the data available to Power BI Desktop, where you can use the data to add tables or visualizations. If you selected <em>DirectQuery,<\/em> you\u2019re loading only the table schema into Power BI Desktop, with no source data imported until it is needed.<\/p>\n\n\n\n<p>As noted earlier, you\u2019re not limited to saving the audit data to log files. You can also save the data to the Application or Security log, but that means taking extra steps to get the data out of the logs and into a digestible format, such as a .csv file.<\/p>\n\n\n\n<p>For example, you can export the data from Windows Event Viewer or the Log File Viewer in SQL Server Management Studio (SSMS), but the resulting format can be difficult to work with, and you could end up with much more data than you need. Another approach is to use PowerShell to retrieve the log data, creating a script that can be scheduled to run automatically. In this way, you have far more control over the output, although there\u2019s still some work involved to get it right.<\/p>\n\n\n\n<p>Regardless of how you make the data available to Power BI Desktop, the more important consideration is data security. The Security log might be the safest approach initially, but once you export the data to files, you\u2019re up against the same issues you face when sending the data directly to log files. The data in those files needs to be fully protected at all times both at-rest and in-motion. There would be little point in implementing an audit strategy to comply with regulatory standards if the auditing process itself puts the data at risk.<\/p>\n\n\n\n<p>Once you\u2019ve made the test audit data available to Power BI Desktop, you can create reports that present the data in ways that offer different insights. Throughout the rest of the article, I show you several approaches that I\u2019ve tried, using both tables and visualizations, in order to demonstrate ways you can present SQL Server Audit data. For details on how to actually create these components, refer to Part 7 of this series, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/power-bi-introduction-building-reports-in-power-bi-desktop-part-7\/\">Building Reports in Power BI Desktop<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-adding-a-table-to-your-report\">Adding a Table to Your Report<\/h2>\n\n\n\n<p>One useful approach to presenting information is to make at least some of the data available in tables. For example, Figure 3 shows a table that includes all the audit data I pulled into Power BI Desktop. You can, of course, filter the data however you need to, such as with Slicers, depending on your specific audit strategy. The auditing that\u2019s implemented for this article is very simple in comparison to the types and amounts of data that you will likely be generating.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"860\" height=\"511\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-201.png\" alt=\"\" class=\"wp-image-81853\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 3. Adding a table and slicers to a report page<\/p>\n\n\n\n<p>In addition to the table, I added three slicers to the report page for filtering the data by users, actions, and successes and failures. For example, Figure 4 shows the data I generated on my system after I filtered the information by the <code>User03<\/code> account and the <code>Failed<\/code> status.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"854\" height=\"503\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-202.png\" alt=\"\" class=\"wp-image-81854\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 4. Using slicers to filter data in a table<\/p>\n\n\n\n<p>When you apply a filter, Power BI updates the data in the table and slicers, based on the selected values. In this way, you have a quick and easy way to access various categories of data, without needing to manually weed through gobs of data or generate a new T-SQL statement each time you want to focus on different information.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-adding-a-matrix-to-your-report\">Adding a Matrix to Your Report<\/h2>\n\n\n\n<p>Another effective way to provide quick insights into the data is to add a matrix that summarizes the data. For example, Figure 5 shows a matrix that displays the number of actions per user and action type. (A <code>ROLLBACK<\/code> statement is issued when a primary statement fails, such as when a user does not have the permissions necessary to run a statement.)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"612\" height=\"328\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-203.png\" alt=\"\" class=\"wp-image-81855\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 5. Adding a matrix and slicers to a report page<\/p>\n\n\n\n<p>On the same report page as the matrix, I added two slicers, one for user accounts and the other for user actions, again allowing me to easily filter the data as needed.<\/p>\n\n\n\n<p>What is especially useful about the matrix is that you can set it up to drill down into the data. In this case, the matrix allows you to drill to the number of actions that have succeeded or failed. For example, Figure 6 shows the matrix after drilling down into all users, although it\u2019s also possible to drill down into specific users.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"612\" height=\"322\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-204.png\" alt=\"\" class=\"wp-image-81856\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 6. Drilling into a matrix<\/p>\n\n\n\n<p>When setting up the drill-down categories in a matrix, you can choose the order in which to present each layer, depending on the type of data and its hierarchical nature. For example, this matrix can also be configured to drill down further into the T-SQL statements associated with each category.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-adding-visualizations-to-your-report\">Adding Visualizations to Your Report<\/h2>\n\n\n\n<p>Power BI Desktop supports a wide variety of visualizations, and you can import even more. You should use whichever visualizations help you and other users best understand the types of audit data you\u2019re collecting. For example, Figure 7 shows a report page with three types of visualizations, each providing a different perspective into the audit data (keeping in mind that you\u2019re working with a limited sample size).<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"844\" height=\"510\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-205.png\" alt=\"\" class=\"wp-image-81857\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 7. Adding visualizations to a report page<\/p>\n\n\n\n<p>For all three visualizations, I filtered out the <code>ROLLBACK<\/code> actions, so the visualizations reflect only the statements that were initiated by the users, rather than those generated as a response to their actions. The visualizations on the left and top-right are clustered bar charts. The clustered bar chart lets you group related data in various ways to provide different perspectives.<\/p>\n\n\n\n<p>For example, the clustered bar chart on the left groups the data by user, and for each user, provides a total for each action type. In this case, the DML actions show the same totals across all three user accounts because I ran the statements the same number of times. Even so, the visualization still demonstrates how you can use the clustered bar chart to provide a quick overview of the data from different perspectives, in this case, the types of statements each user has tried to run.<\/p>\n\n\n\n<p>The clustered bar chart at the top right also groups the data by user, but then provides a total of statement successes and failures for each account, making it possible to see which users might be attempting to run statements that they\u2019re not permitted to run.<\/p>\n\n\n\n<p>The bottom right visualization is a basic donut chart. The visualization presents the same data as the clustered bar chart above it, but in a different way. If you hover over one of the elements, for example, you\u2019ll get the percentage of total actions. What all this points to is that you can try out different visualizations against the same data to see which ones provide the best perspectives into the underlying information.<\/p>\n\n\n\n<p>When you place elements on the same report page, Power BI automatically ties them together. In this way, if you select an element in one visualization, the other visualizations will reflect the selection. For example, if you click the <em>Failed<\/em> bar for <em>User03<\/em> in the top right clustered bar chart, the corresponding data elements in the other visualizations will be selected, resulting in the nonrelated elements being grayed out.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-adding-a-gauge-to-your-report\">Adding a Gauge to Your Report<\/h2>\n\n\n\n<p>Power BI Desktop also lets you add elements such as gauges, cards, and key performance indicators (KPIs) to a report. For example, I added the gauge shown in Figure 8 to display the number of <code>ROLLBACK<\/code> statements that were executed in response to the users\u2019 failed attempts to run T-SQL statements.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"320\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/11\/word-image-206.png\" alt=\"\" class=\"wp-image-81858\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"caption\">Figure 8. Adding a gauge to a report page<\/p>\n\n\n\n<p>I also added a slicer for the user accounts to make it easy to see whether individual users are hitting the specified threshold. In Figure 8, the <code>User03<\/code> account is selected in the slicer, so the gauge shows only the number of rollbacks that pertain to that user. The gauge also specifies a target of <em>50,<\/em> but you can specify any target value.<\/p>\n\n\n\n<p>The reason the target value is significant is that you can set alerts based on that value in the Power BI service (something you cannot do in Power BI Desktop). To use this feature, however, you must have a Power BI Pro license, which allows you to set up alerts that notify you regularly about potential issues. You can also set up alerts that generate email notifications on a regular basis. Plus, you can add alerts to card and KPI visuals.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-visualizing-audit-data-in-power-bi\">Visualizing Audit Data in Power BI<\/h2>\n\n\n\n<p>Using Power BI to visualize SQL Server Audit data can provide you will a powerful tool for reviewing the logged information in a quick and efficient manner. What I\u2019ve covered here only scratches the surface. There\u2019s far more to SQL Server Audit and Power BI Desktop, and both topics deserve much more attention.<\/p>\n\n\n\n<p>But this article should at least offer you a good starting point for understanding what it takes to use Power BI to work with SQL Server Audit data. It\u2019s up to you to decide how far you want to take this and how extensively you might want to use the Power BI service, in addition to Power BI Desktop, keeping in mind the importance of protecting the audit data wherever it resides.<\/p>\n\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: Power BI Introduction: Visualizing SQL Server Audit Data \u2014 Part 9<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What SQL Server views contain audit data for Power BI?<\/h3>\n            <div class=\"faq-answer\">\n                <p>SQL Server Audit stores data in the Windows Security Event Log, a binary file target, or the Application Log, depending on configuration. To query audit data, use the sys.fn_get_audit_file function: SELECT * FROM sys.fn_get_audit_file(&#8216;C:AuditLogs*.sqlaudit&#8217;, DEFAULT, DEFAULT). For SQL Server Audit configured to write to a file target, this function returns one row per audit event with columns for action_id, object_name, database_name, statement, and server_principal_name. Use this query as the Power BI data source in a native database query connection.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. How do I connect Power BI Desktop to SQL Server Audit data?<\/h3>\n            <div class=\"faq-answer\">\n                <p>In Power BI Desktop, select Get Data &gt; SQL Server. Enter the server and database name, then under Advanced Options, enter a native database query using sys.fn_get_audit_file or a view built on top of it. This returns the audit events as a Power BI table that can be modelled, filtered, and visualised. Alternatively, create a SQL Server view or stored procedure that queries the audit file and connect Power BI to that view &#8211; this abstracts the file path and makes the connection simpler to manage as the audit file location changes.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. What Power BI visualisations work well for SQL Server Audit data?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Effective visualisations for audit data: bar or column charts by action_id (to see which operations are most frequent); time series line charts for activity over time; tables or matrices showing the most recent events with filters for specific users, objects, or action types; gauges for KPIs like failed login count or DDL changes in a period; and cards for summary counts (total events, unique users). Adding slicers for server_principal_name and object_name lets users drill into specific users&#8217; or tables&#8217; activity.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. How do I monitor database security with Power BI?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Combine SQL Server Audit data with Power BI to build a security monitoring dashboard: create a scheduled refresh dataset that pulls the latest audit events daily; add visuals for failed logins, privileged account activity, DDL changes, and access to sensitive tables; set up Power BI alerts on calculated measures (e.g., if failed logins today &gt; threshold, trigger an alert notification). For continuous monitoring, consider Power BI Embedded with automated refresh, or use Azure Sentinel for real-time security event analysis with Power BI as the reporting layer.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>Connect Power BI Desktop to SQL Server Audit data to create interactive reports. Covers test database setup, DML\/DDL audit generation, Power BI data connections, and building tables, matrices, charts, and gauges from audit logs.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":true,"footnotes":""},"categories":[143528,143514],"tags":[68855],"coauthors":[6779],"class_list":["post-81849","post","type-post","status-publish","format-standard","hentry","category-bi-sql-server","category-data-privacy-and-protection","tag-sql-provision"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81849","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=81849"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81849\/revisions"}],"predecessor-version":[{"id":109869,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81849\/revisions\/109869"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=81849"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=81849"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=81849"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=81849"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}