[CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DatabaseName, [string]$LoginName = "tplus_reader", [string]$ContainerName = "ys-tplus-sqlserver-dev", [Parameter(Mandatory = $true)] [SecureString]$SaPassword, [Parameter(Mandatory = $true)] [SecureString]$ReaderPassword ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" . (Join-Path $PSScriptRoot "container_sql.ps1") function Convert-SecureStringToPlainText([SecureString]$Value) { $ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($Value) try { return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr) } } if ($LoginName -notmatch '^[A-Za-z_][A-Za-z0-9_]{0,63}$') { throw "只读账号名只能包含字母、数字和下划线,且不能以数字开头。" } $readerPasswordText = Convert-SecureStringToPlainText $ReaderPassword $safeDatabaseIdentifier = $DatabaseName.Replace("]", "]]" ) $safeDatabaseLiteral = $DatabaseName.Replace("'", "''") $safeLoginIdentifier = $LoginName.Replace("]", "]]" ) $safeReaderPassword = $readerPasswordText.Replace("'", "''") $sql = @" IF DB_ID(N'$safeDatabaseLiteral') IS NULL BEGIN ;THROW 51000, N'目标数据库不存在,已停止配置,未修改任何库。', 1; END; DECLARE @initialReadonly bit = CASE WHEN CAST(DATABASEPROPERTYEX(N'$safeDatabaseLiteral', 'Updateability') AS nvarchar(30)) = N'READ_ONLY' THEN 1 ELSE 0 END; IF @initialReadonly = 1 ALTER DATABASE [$safeDatabaseIdentifier] SET READ_WRITE; IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'$safeLoginIdentifier') BEGIN CREATE LOGIN [$safeLoginIdentifier] WITH PASSWORD = N'$safeReaderPassword', CHECK_POLICY = ON; END; USE [$safeDatabaseIdentifier]; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'$safeLoginIdentifier') BEGIN CREATE USER [$safeLoginIdentifier] FOR LOGIN [$safeLoginIdentifier]; END; IF NOT EXISTS ( SELECT 1 FROM sys.database_role_members rm JOIN sys.database_principals r ON r.principal_id = rm.role_principal_id JOIN sys.database_principals u ON u.principal_id = rm.member_principal_id WHERE r.name=N'db_datareader' AND u.name=N'$safeLoginIdentifier' ) EXEC sp_addrolemember N'db_datareader', N'$safeLoginIdentifier'; DENY INSERT, UPDATE, DELETE TO [$safeLoginIdentifier]; IF @initialReadonly = 1 ALTER DATABASE [$safeDatabaseIdentifier] SET READ_ONLY; "@ Invoke-TPlusContainerSql -ContainerName $ContainerName -Sql $sql -SaPassword $SaPassword Write-Host "只读账号已配置:$LoginName(未输出密码)。仅当目标库初始为 READ_ONLY 时才恢复只读;初始为可写的库保持可写。"