SQL Server · DCL · SCADA Security Lab

SQL DCL Scripting for Automation

Learn to secure industrial SQL Server access with GRANT, DENY and REVOKE—then build and verify a least-privilege database role for SCADA Runtime.

Principals GRANT and DENY Custom roles Permission testing

Learning Overview

Level: SQL security basicsFormat: Blog + practical labModel: least privilegeEstimated time: 150 minutes

Prerequisites / What You’ll Need

  • A disposable SQF_DB practice database
  • SSMS and an account allowed to manage test permissions
  • dbo.vw_ChargeSummary from the DDL guide
  • dbo.usp_RecordEvent from the DML guide
  • A documented list of Runtime read/write operations
Core idea

A SCADA identity should be able to perform its approved process workflow and nothing more. Prefer custom database roles, approved views and stored procedures over db_owner, db_datareader, db_datawriter or direct table-wide permissions.

  • GRANT allows, DENY explicitly blocks and REVOKE removes an explicit decision.
  • Assign policy to roles and membership to users.
  • Give Runtime EXECUTE on controlled write procedures instead of unrestricted table writes.
  • Test effective permissions as the target user before deployment.

SQL DCL for SCADA and Industrial Automation

This guide covers SQL Server GRANT DENY REVOKE, SCADA database permissions, custom SQL roles, least-privilege Runtime access and a complete DCL security lab.

1. What Is DCL?

DCL means Data Control Language. It manages who can perform an action on a SQL Server securable.

StatementMeaningAutomation example
GRANTAllow a permissionAllow Runtime to execute usp_RecordEvent
DENYExplicitly block a permissionBlock direct deletion from event tables
REVOKERemove an explicit GRANT or DENYReturn permission evaluation to role inheritance

CREATE USER, CREATE ROLE and ALTER ROLE prepare security principals and memberships; they are security-management DDL used alongside DCL.

2. Understand the Authorization Model

A permission decision connects three elements:

  1. Principal: who requests access—login, database user or role.
  2. Securable: what is protected—server, database, schema, table, view or procedure.
  3. Permission: which action—SELECT, INSERT, UPDATE, DELETE, EXECUTE or another permission.
GRANT EXECUTE
ON OBJECT::[dbo].[usp_RecordEvent]
TO [scada_runtime_role];

Here the principal is scada_runtime_role, the securable is the procedure and the permission is EXECUTE.

3. Login, Database User and Role

PrincipalScopePurpose
LoginSQL Server instanceAuthenticates to the Database Engine
Database userOne databaseRepresents an identity inside that database
Database roleOne databaseGroups users under one permission policy
Server roleSQL Server instanceGroups server-level permissions

For SQL Server, a login is normally mapped to a user in each required database. For the safe lab, use a contained test user without a login:

USE [SQF_DB];
GO

CREATE USER [scada_lab_user] WITHOUT LOGIN;
GO
Production identity: prefer an approved Windows/managed service identity where the platform supports it. Never embed an administrator password in a WinCC script, HMI project, source file or connection string visible to operators.

4. Securables and Permission Scope

Permissions form a hierarchy. A grant at a broad scope can flow to contained securables.

ScopeExampleRisk
ServerVIEW SERVER STATEAffects the entire SQL Server instance
DatabaseCONNECT, SELECT ALL USER SECURABLESBroad database visibility
SchemaSELECT ON SCHEMA::reportingApplies to schema-contained objects
ObjectSELECT ON dbo.vw_ChargeSummaryNarrow and easy to review
ColumnSELECT ON dbo.Table(Column)Fine-grained but complex to maintain

Start at object scope. Move to schema scope only when every current and future object in that schema is intended for the same audience.

5. GRANT: Allow an Approved Action

GRANT SELECT
ON OBJECT::[dbo].[vw_ChargeSummary]
TO [scada_runtime_role];

GRANT EXECUTE
ON OBJECT::[dbo].[usp_RecordEvent]
TO [scada_runtime_role];

Use explicit securable classes such as OBJECT:: and SCHEMA:: in deployment scripts. They make the intended permission scope easier to review.

Least privilege

If Runtime only calls a write procedure, grant EXECUTE on that procedure. Do not also grant direct INSERT, UPDATE and DELETE on the underlying tables without a separate requirement.

6. DENY: Explicitly Block a Permission

DENY DELETE
ON OBJECT::[dbo].[tblEvent]
TO [scada_runtime_role];

DENY is useful when a role inherits a broader grant that must be blocked. However, no grant is simpler than a deny when you control the full role design.

Use DENY carefully: inherited roles can make results surprising, and a table-level DENY versus column-level GRANT has a documented backward-compatibility exception. Avoid complicated mixed-scope rules when a clean custom role can express the policy.

Do not casually deny permissions to public; every database user belongs to that role and the effect can be much wider than intended.

7. REVOKE: Remove an Explicit Decision

-- Remove an explicit GRANT or DENY at this scope.
REVOKE DELETE
ON OBJECT::[dbo].[tblEvent]
FROM [scada_runtime_role];

REVOKE does not mean “block”. After revocation, the user may still receive the permission through another role or a broader grant. Recalculate effective permissions after every change.

Current actionNext statementResult
Explicit GRANTREVOKEExplicit allow removed
Explicit DENYREVOKEExplicit block removed
No explicit permissionNoneRole and hierarchy determine access

8. How SQL Server Calculates Effective Permissions

Effective access can come from the user, one or more roles, broader permission scopes, ownership or execution context. In general, an applicable DENY overrides a GRANT, subject to documented exceptions.

SELECT
    HAS_PERMS_BY_NAME(N'dbo.vw_ChargeSummary', N'OBJECT', N'SELECT') AS CanReadSummary,
    HAS_PERMS_BY_NAME(N'dbo.usp_RecordEvent', N'OBJECT', N'EXECUTE') AS CanRecordEvent,
    HAS_PERMS_BY_NAME(N'dbo.tblEvent', N'OBJECT', N'DELETE') AS CanDeleteEvents;

Run this under the target execution context. A result of 1 means permission, 0 means no permission and NULL can indicate an invalid securable/permission context.

9. Create a Custom SCADA Role

CREATE ROLE [scada_runtime_role] AUTHORIZATION [dbo];
GO

ALTER ROLE [scada_runtime_role]
ADD MEMBER [scada_lab_user];
GO

Roles express job functions: scada_runtime_role, report_reader_role, maintenance_engineer_role and deployment_role. Avoid creating slightly different permissions directly on dozens of individual users.

-- Remove membership during offboarding or role change.
ALTER ROLE [scada_runtime_role]
DROP MEMBER [scada_lab_user];

10. Design Read-Only Reporting Access

Expose approved columns through a view and grant access to the view rather than every base table.

CREATE ROLE [report_reader_role] AUTHORIZATION [dbo];

GRANT SELECT
ON OBJECT::[dbo].[vw_ChargeSummary]
TO [report_reader_role];

When the view and referenced objects share an owner, ownership chaining can allow view access without separate base-table permissions. Dynamic SQL and cross-database access can break that simple chain, so test the exact module.

11. Design Execute-Only Runtime Writes

Place validation and DML inside a stored procedure, then grant only EXECUTE.

GRANT EXECUTE
ON OBJECT::[dbo].[usp_RecordEvent]
TO [scada_runtime_role];

-- No direct INSERT/UPDATE/DELETE grants are required for a normal ownership chain.

This design limits the write shape, validates equipment and types, returns the created identity and lets the database owner change table internals without granting Runtime broader access.

Module review: ownership chaining does not automatically make unsafe dynamic SQL secure. Parameterize statements and use certificate signing or a carefully reviewed execution context when a module needs permissions not available through the normal chain.

12. Use Schema-Level Grants Deliberately

CREATE SCHEMA [reporting] AUTHORIZATION [dbo];
GO

GRANT SELECT
ON SCHEMA::[reporting]
TO [report_reader_role];

A schema-level grant can simplify administration for a governed reporting schema. It also applies to appropriate future objects added to that schema. Do not use GRANT CONTROL ON SCHEMA merely to provide read access.

13. Why Broad Fixed Roles Are Risky

RoleCapabilitySCADA concern
db_ownerFull database controlRuntime can change security and schema
db_datareaderRead all user tables/viewsExposes unrelated plant, HR or configuration data
db_datawriterWrite all user tablesBypasses stored-procedure validation
sysadminUnrestricted server controlNever appropriate for an ordinary HMI/SCADA service

Custom roles make the access matrix visible. Fixed roles can be appropriate for DBA operations, but not as a shortcut for normal Runtime connectivity.

14. WITH GRANT OPTION

-- Powerful delegation; normally not for Runtime identities.
GRANT SELECT
ON OBJECT::[dbo].[vw_ChargeSummary]
TO [report_security_admin]
WITH GRANT OPTION;

WITH GRANT OPTION lets the grantee grant the same permission to others. Restrict it to controlled security administrators. Revoking a permission that was delegated can require CASCADE; understand the downstream grants before changing it.

15. Test with EXECUTE AS and REVERT

EXECUTE AS USER = N'scada_lab_user';

SELECT USER_NAME() AS [ExecutionUser];
SELECT
    HAS_PERMS_BY_NAME(N'dbo.vw_ChargeSummary', N'OBJECT', N'SELECT') AS CanReadSummary,
    HAS_PERMS_BY_NAME(N'dbo.usp_RecordEvent', N'OBJECT', N'EXECUTE') AS CanRecordEvent,
    HAS_PERMS_BY_NAME(N'dbo.tblEvent', N'OBJECT', N'DELETE') AS CanDeleteEvents;

REVERT;
SELECT USER_NAME() AS [RestoredUser];
Expected: read-summary and record-event return 1; direct delete returns 0; REVERT restores the administrator context.
Always REVERT: keep impersonation tests in a short, dedicated batch and restore the original context even when a test fails.

16. Audit Roles and Explicit Permissions

SELECT
    rolep.[name] AS [RoleName], memberp.[name] AS [MemberName]
FROM sys.database_role_members AS drm
JOIN sys.database_principals AS rolep
    ON rolep.[principal_id] = drm.[role_principal_id]
JOIN sys.database_principals AS memberp
    ON memberp.[principal_id] = drm.[member_principal_id]
ORDER BY rolep.[name], memberp.[name];

SELECT
    grantee.[name] AS [PrincipalName],
    p.[state_desc], p.[permission_name], p.[class_desc],
    CASE
        WHEN p.[class] = 1 THEN OBJECT_SCHEMA_NAME(p.[major_id])
        WHEN p.[class] = 3 THEN SCHEMA_NAME(p.[major_id])
    END AS [SecurableSchema],
    CASE
        WHEN p.[class] = 1 THEN OBJECT_NAME(p.[major_id])
        WHEN p.[class] = 3 THEN N'(schema)'
    END AS [SecurableName]
FROM sys.database_permissions AS p
JOIN sys.database_principals AS grantee
    ON grantee.[principal_id] = p.[grantee_principal_id]
WHERE grantee.[name] IN (N'scada_runtime_role', N'report_reader_role')
ORDER BY grantee.[name], p.[class_desc], p.[permission_name];

Catalog views show explicit permissions, not a complete flattened result of every inheritance path or fixed-role capability. Combine catalog review with impersonation and HAS_PERMS_BY_NAME.

17. Protect Automation Credentials

  • Prefer Windows or managed identities and dedicated service accounts.
  • Do not reuse personal DBA accounts for Runtime.
  • Store secrets in an approved secret store, not in HTML, VBS, Python source or HMI text fields.
  • Use encrypted connections and validate the SQL Server certificate.
  • Rotate credentials under a tested procedure that avoids production downtime.
  • Disable or remove accounts immediately during decommissioning.
  • Log authentication failures and permission changes without exposing passwords.

18. Separate Runtime, Reporting and Deployment

IdentityTypical permissionsMust not receive by default
SCADA RuntimeExecute approved write procedures; select approved live viewsDDL, security administration, unrestricted delete
Reporting serviceSelect approved reporting views/proceduresWrite permissions
Migration/deploymentTime-limited DDL/DCL required by releasePermanent use by Runtime
DBA/security administratorControlled administrative accessEmbedded application use

This separation limits blast radius and creates a clearer audit trail.

19. Common DCL Errors and Fixes

SymptomLikely causeCheck or fix
Login succeeds but database access failsNo mapped user or CONNECT pathInspect login/user mapping and database state
Permission denied after GRANTApplicable DENY, wrong database/schema or execution contextTest as user and inspect all role memberships
REVOKE did not block accessPermission is inherited elsewhereFind role/broader-scope grants; use DENY only if policy requires it
View works but base table failsIntended ownership chainConfirm view is the supported interface
Procedure fails inside dynamic SQLOwnership chain not applied to dynamic batchReview module signing/execution context and parameterization
User appears overprivilegedFixed-role membership or broad schema/database grantRemove broad membership and rebuild custom role
Cannot drop user/roleOwns objects/schema or still has membersTransfer ownership and remove memberships first

20. Complete Practical DCL Lab

Hands-on
Lab safety
  • Use a disposable practice database only.
  • Run setup as a test security administrator.
  • Do not copy lab principals into production unchanged.
1

Write the access matrix

WorkflowInterfacePermission
Read charge summarydbo.vw_ChargeSummarySELECT
Record an eventdbo.usp_RecordEventEXECUTE
Delete event historyNot exposedNone
Change schema/securityNot exposedNone
2

Create the lab user and role

CREATE USER [scada_lab_user] WITHOUT LOGIN;
CREATE ROLE [scada_runtime_role] AUTHORIZATION [dbo];
ALTER ROLE [scada_runtime_role] ADD MEMBER [scada_lab_user];
3

Grant only approved interfaces

GRANT SELECT ON OBJECT::[dbo].[vw_ChargeSummary] TO [scada_runtime_role];
GRANT EXECUTE ON OBJECT::[dbo].[usp_RecordEvent] TO [scada_runtime_role];
4

Prove allowed and blocked operations

Use EXECUTE AS USER and HAS_PERMS_BY_NAME. Confirm view select and procedure execute are allowed while direct table delete and schema alteration are not.

5

Inspect the catalog

Query sys.database_role_members and sys.database_permissions. Save the result with the approved access matrix.

6

Revoke and retest one permission

REVOKE SELECT ON OBJECT::[dbo].[vw_ChargeSummary] FROM [scada_runtime_role];
-- Retest as scada_lab_user: CanReadSummary should now be 0.
GRANT SELECT ON OBJECT::[dbo].[vw_ChargeSummary] TO [scada_runtime_role];
Lab complete

You can now create a custom role, grant narrow object permissions, explain DENY versus REVOKE, impersonate a test user and audit explicit access.

DCL Deployment Checklist

  1. Identify the real service identity.
  2. List workflows, not guessed permissions.
  3. Expose approved views and stored procedures.
  4. Create a custom database role.
  5. Grant at the narrowest maintainable scope.
  6. Avoid broad fixed roles and delegation rights.
  7. Test as the target user.
  8. Audit membership and explicit permissions.
  9. Store approval evidence and rollback DCL.
  10. Review access periodically and on personnel/system changes.

Further Reading

Frequently Asked Questions

What is DCL in SQL Server?

Data Control Language manages authorization through GRANT, DENY and REVOKE.

What is the difference between DENY and REVOKE?

DENY explicitly blocks a permission. REVOKE removes an explicit GRANT or DENY, after which inherited permissions determine access.

Should a SCADA account be db_owner?

Normally no. Grant only the approved view and stored-procedure permissions required by Runtime.

Why use database roles?

Roles centralize policy, simplify membership changes and make security audits easier.

How do I test permissions?

Use EXECUTE AS USER, HAS_PERMS_BY_NAME and catalog views in a test database, then always issue REVERT.

Get the SQL Reporting and Automation Syllabus

Share your details and a Softwell advisor will contact you with practical SQL, SCADA and industrial reporting training options.

Secure SQL access for industrial automation

Join practical online, classroom or corporate SQL Server, reporting and SCADA integration training.

Request Course Details
Verified learning pathway

Discuss SQL DCL and Automation Security Training

Explore practical SQL Server, PLC/SCADA integration and industrial data training options.

Content reviewed: 4 August 2026

☎ Call WhatsApp ✉ Email Enquire Now