login 脚本原先在结尾无条件 SET READ_ONLY:库名误指向其他 可写库时会把它锁成只读。改为开头校验库存在(不存在即中止) 并记录初始 Updateability,结尾仅当初始为 READ_ONLY 时恢复。 inventory 脚本的库名校验统一为 restore 脚本的字符白名单。 Co-Authored-By: Claude Code <noreply@anthropic.com>
65 lines
2.7 KiB
PowerShell
65 lines
2.7 KiB
PowerShell
[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 时才恢复只读;初始为可写的库保持可写。"
|