jas*_*son 5 sql-server maintenance sql-server-2012
有没有办法找出过去 90 天内未登录 SQL Server 或访问数据库的登录名?
您可以使用扩展事件会话捕获此信息:
CREATE EVENT SESSION [Audit_Logon] ON SERVER
ADD EVENT sqlserver.LOGIN (
SET collect_database_name = (1)
,collect_options_text = (0)
ACTION(
sqlserver.client_app_name
,sqlserver.client_hostname
,sqlserver.server_principal_name
)
)
WITH (
MAX_MEMORY = 4096 KB
,EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS
,MAX_DISPATCH_LATENCY = 30 SECONDS
,MAX_EVENT_SIZE = 0 KB
,MEMORY_PARTITION_MODE = NONE
,TRACK_CAUSALITY = OFF
,STARTUP_STATE = ON
)
GO
Run Code Online (Sandbox Code Playgroud)
然后,您可以流式传输会话中捕获的事件并使用 powershell 脚本处理它们。
首先,您需要几个表来存储结果。在这种情况下,您可以使用临时表来临时存储事件,并使用目标表以汇总形式存储事件。您对单个事件不感兴趣,您只需要记录上次成功登录的时间。如果这对您有意义,您可以通过一些有意义的属性对事件进行分组:
在master以下位置创建这些表:
CREATE TABLE [dbo].[AuditLogin](
[LoggingID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
[LoginName] [sysname] NOT NULL,
[HostName] [varchar](100) NULL,
[NTUserName] [varchar](100) NULL,
[NTDomainName] [varchar](100) NULL,
[ApplicationName] [varchar](340) NULL,
[DatabaseName] [nvarchar](4000) NULL,
[FirstSeen] [datetime] NULL,
[LastSeen] [datetime] NULL,
[LogonCount] [bigint] NULL,
)
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_AuditLogon] ON [dbo].[AuditLogin]
(
[LoginName] ASC,
[HostName] ASC,
[ApplicationName] ASC,
[DatabaseName] ASC
)
GO
CREATE TABLE [dbo].[AuditLogin_Staging](
[event_date] [datetime] NULL,
[original_login] [nvarchar](128) NULL,
[host_name] [nvarchar](128) NULL,
[program_name] [nvarchar](255) NULL,
[database_name] [nvarchar](128) NULL
)
GO
Run Code Online (Sandbox Code Playgroud)
然后您将需要一个存储过程来将数据从暂存表合并到目标表:
USE master
GO
CREATE PROCEDURE [dbo].[spConsolidateAuditLogin]
AS
BEGIN
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#AuditLogin_Staging') IS NOT NULL
DROP TABLE #AuditLogin_Staging;
CREATE TABLE #AuditLogin_Staging(
[event_date] [datetime] NULL,
[original_login] [nvarchar](128) NULL,
[host_name] [nvarchar](128) NULL,
[program_name] [nvarchar](255) NULL,
[database_name] [nvarchar](128) NULL
);
DELETE
FROM dbo.AuditLogin_Staging
OUTPUT DELETED.* INTO #AuditLogin_Staging;
MERGE INTO [AuditLogin] AS AuditLogin
USING (
SELECT MAX(event_date), original_login, host_name, program_name, database_name
,NtDomainName = CASE SSP.type WHEN 'U' THEN LEFT(SSP.name,CHARINDEX('\',SSP.name,1)-1) ELSE '' END
,NtUserName = CASE SSP.type WHEN 'U' THEN RIGHT(SSP.name,LEN(ssp.name) - CHARINDEX('\',SSP.name,1)) ELSE '' END
,COUNT(*)
FROM #AuditLogin_Staging AS ALA
INNER JOIN sys.server_principals AS SSP
ON ALA.original_login = SSP.name
GROUP BY original_login, host_name, program_name, database_name
,CASE SSP.type WHEN 'U' THEN LEFT(SSP.name,CHARINDEX('\',SSP.name,1)-1) ELSE '' END
,CASE SSP.type WHEN 'U' THEN RIGHT(SSP.name,LEN(ssp.name) - CHARINDEX('\',SSP.name,1)) ELSE '' END
) AS src (PostTime,LoginName,HostName,ApplicationName,DatabaseName,NtDomainName,NtUserName,LogonCount)
ON AuditLogin.ApplicationName = src.ApplicationName
AND AuditLogin.LoginName = src.LoginName
AND AuditLogin.HostName = src.HostName
AND AuditLogin.DatabaseName = src.DatabaseName
WHEN MATCHED THEN
UPDATE SET
LastSeen = GETDATE()
,LogonCount += src.LogonCount
WHEN NOT MATCHED THEN
INSERT (
LoginName
,HostName
,NTUserName
,NTDomainName
,ApplicationName
,DatabaseName
,FirstSeen
,LastSeen
,LogonCount
)
VALUES (
src.LoginName
,src.HostName
,src.NTDomainName
,src.NTUserName
,src.ApplicationName
,src.DatabaseName
,src.PostTime
,src.PostTime
,src.LogonCount
);
END
Run Code Online (Sandbox Code Playgroud)
有趣的部分是从会话流中读取事件的 powershell 脚本。将脚本另存为c:\capture_logon_events.ps1:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$servername
)
sl $Env:Temp
Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\Shared\Microsoft.SqlServer.XEvent.Linq.dll'
$connectionString = 'Data Source=' + $servername + '; Initial Catalog = master; Integrated Security = SSPI'
$SessionName = "Audit_Logon"
# create a DataTable to hold login information in memory
$queue = New-Object -TypeName System.Data.DataTable
$queue.TableName = $SessionName
[Void]$queue.Columns.Add("event_date",[DateTime])
[Void]$queue.Columns.Add("original_login",[String])
[Void]$queue.Columns.Add("host_name",[String])
[Void]$queue.Columns.Add("program_name",[String])
[Void]$queue.Columns.Add("database_name",[String])
$last_dump = [DateTime]::Now
# connect to the Extended Events session
[Microsoft.SqlServer.XEvent.Linq.QueryableXEventData] $events = New-Object -TypeName Microsoft.SqlServer.XEvent.Linq.QueryableXEventData `
-ArgumentList @($connectionString, $SessionName, [Microsoft.SqlServer.XEvent.Linq.EventStreamSourceOptions]::EventStream, [Microsoft.SqlServer.XEvent.Linq.EventStreamCacheOptions]::DoNotCache)
$events | % {
$currentEvent = $_
$database_name = $currentEvent.Fields["database_name"].Value
if($client_app_name -eq $null) { $client_app_name = [string]::Empty }
$original_login_name = $currentEvent.Actions["server_principal_name"].Value
$client_app_name = $currentEvent.Actions["client_app_name"].Value
$client_host_name = $currentEvent.Actions["client_hostname"].Value
$current_row = $queue.Rows.Add()
$current_row["database_name"] = $database_name
$current_row["program_name"] = $client_app_name
$current_row["host_name"] = $client_host_name
$current_row["original_login"] = $original_login_name
$current_row["event_date"] = [DateTime]::Now
$ts = New-TimeSpan -Start $last_dump -End (get-date)
# Dump to database every 1 minutes
if($ts.TotalMinutes -gt 1) {
$last_dump = [DateTime]::Now
# BCP data to the staging table master.dbo.master.dbo.AuditLogin_Staging
$bcp = New-Object -TypeName System.Data.SqlClient.SqlBulkCopy -ArgumentList @($connectionString)
$bcp.DestinationTableName = "master.dbo.AuditLogin_Staging"
$bcp.Batchsize = 1000
$bcp.BulkCopyTimeout = 0
$bcp.WriteToServer($queue)
$queue.Rows.Clear()
}
}
Run Code Online (Sandbox Code Playgroud)
该脚本可以由作业中的 SQLAgent powershell 步骤运行。创建一个作业,将其设置为在 SQLAgent 启动时运行,使用以下代码添加一个 powershell 步骤:
powershell -File C:\capture_logon_events.ps1 -servername $(ESCAPE_DQUOTE(SRVR))
Run Code Online (Sandbox Code Playgroud)
然后,您需要另一个作业来使用存储过程合并事件:将其设置为每 5 分钟运行一次,并添加一个 T-SQL 步骤并调用合并存储过程。
EXEC [dbo].[spConsolidateAuditLogin]
Run Code Online (Sandbox Code Playgroud)
你已经完成了。看起来很复杂,其实不然。
/sf/ask/133365921/的可能重复
此查询应显示上次登录日期/时间和登录名。通过查询 sys.dm_exec_sessions 可以收集更多信息。
select max (login_time)as last_login_time, login_name from sys.dm_exec_sessions
group by login_name;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1781 次 |
| 最近记录: |