Requirement: I am looking at how to automate the cloning process from Prod to dev every day
Either Python or Stored Procedure
You can clone a database with a single-line SQL statement; however, I tested running that in a SQL stored procedure and it will not run create statements in SQL stored procedures. A JavaScript stored procedure will work.
Since all Snowflake tasks require a stored procedure to run, you'll need a JavaScript stored procedure if you want to use tasks.
Here's a sample:
-- Recommend you do not store the SP in a database you'll be cloning
create or replace procedure UTIL_DB.PUBLIC.CLONE_MY_DB()
returns string
language javascript
as
$$
return executeNonQuery('create or replace database TEST2 clone TEST1');
function executeNonQuery(queryString) {
var out;
cmd1 = {sqlText: queryString};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
rs.next();
return rs.getColumnValue('status');
return out;
};
$$;
-- Make sure the SP works
drop database if exists TEST2;
call UTIL_DB.PUBLIC.CLONE_MY_DB();
-- Create a task calling the SP.
-- Use CRON syntax to get productionized
-- https://docs.snowflake.com/en/sql-reference/sql/create-task.html
create task MY_CLONE_TASK
warehouse = TEST
schedule = '1440 minute'
as call UTIL_DB.PUBLIC.CLONE_MY_DB();
Remember that your task is not enabled by default. You'll need to do something like this for the role you want to run the task:
use role ACCOUNTADMIN;
grant execute task on account to role SYSADMIN;
use role SYSADMIN;
alter task UTIL_DB.PUBLIC.MY_CLONE_TASK resume;
Since you make reference to cloning, I will assume that your Prod and Dev environments are in the same Snowflake account. You could this pretty easily with a task that simply drops the DEV database and then clones the PROD database. I don't think you need an SP or Python for this.
Related
I have created a Procedure in snowflake
I want to give read-privileges of that stored procedure in snowflake to some other user/role.
GRANT USAGE ON PROCEDURE
get_column_scale(float)
TO ROLE other_role_name_here;
I believe the above command would also give the write access, which I don't want. Is there a way in which only read access can be granted
The only privilege to assign to a procedure is usage, so if you want a role to be able to read the definition for the procedure but not run it, then you can use a stored procedure defined to execute with owner's rights:
create or replace procedure read_procedure(PROCEDURE_PATH string)
returns string
language javascript
execute as owner
as
$$
let rs = snowflake.execute({sqlText: `select get_ddl('procedure', ?) as DDL`, binds:[PROCEDURE_PATH]});
rs.next();
return rs.getColumnValue('DDL');
$$;
create or replace procedure FOO(S string)
returns string
language javascript
as
$$
return S;
$$;
grant usage on procedure READ_PROCEDURE(string) to role MY_ROLE;
call read_procedure('FOO(string)');
In this setup, make sure that MY_ROLE has usage on procedure read_procedure(string) but does not have usage on procedure foo(string). That way the role will be able to see the definition of foo(string) without the privilege required to execute it (usage).
Note that since the read_procedure(string) procedure runs with owner's rights, anyone calling it will be able to read any procedure that the owner's role can read.
Edit: You can also read the SP in parts in the INFORMATION_SCHEMA.PROCEDURES view. You can grant select on that view without granting USAGE on the procedure.
I am working with a Snowflake stored procedure that will take a view that is changed from one database to another one. I am trying
var sqlCommand = `
SELECT a.OBJECT_NAME, a.OBJECT_SCHEMA, a.OBJECT_TYPE, d.VIEW_DEFINITION
FROM VIEW_OBJECT_LIST a
INNER JOIN DB_DEV.INFORMATION_SCHEMA.VIEWS d ON a.OBJECT_NAME = d.TABLE_NAME AND a.OBJECT_SCHEMA = d.TABLE_SCHEMA
INNER JOIN DB_QA.INFORMATION_SCHEMA.VIEWS q ON a.OBJECT_NAME = q.TABLE_NAME AND a.OBJECT_SCHEMA = q.TABLE_SCHEMA AND d.VIEW_DEFINITION != q.VIEW_DEFINITION;`;
var viewList = snowflake.createStatement({ sqlText: sqlCommand}).execute();
while(viewList.next()){
var sql = viewList.VIEW_DEFINITION;
var sql = 'USE DB_QA; ' || sql;
snowflake.createStatement({ sqlText: sql }).execute();
}
but getting the error message
Unsupported statement type 'USE'. At Statement.execute
Is there a way in a Snowflake stored procedure to be able to run a USE statement as part of a call to the Snowflake API?
what is the execute permission you are using while creating this stored procedure?
If you are using "EXECUTE AS OWNER" in the create procedure statement then you might not be able to use the statements other than below statements :->
Restrictions on SQL Statements
Although caller’s rights stored procedures can execute any SQL statement that the caller has sufficient privileges to execute outside a stored procedure, owner’s rights stored procedures can call only a subset of SQL statements.
The following SQL statements can be called from inside an owner’s rights stored procedure:
SELECT.
DML.
DDL. (See above for restrictions on the ALTER USER statement.)
GRANT/REVOKE.
Variable assignment.
DESCRIBE and SHOW. (See limitations documented above.)
Other SQL statements cannot be called from inside an owner’s rights stored procedure.
Please try creating same procedure using "EXECUTE AS CALLER" this allows snowflake native sql commands like LIST, USE DATABASE,RM, etc.
I believe the only issue is that you have not defined what DB_QA is in your USE statement. I assume its the name of a database, so you just need to modify that line to be:
var sql = 'USE DATABASE DB_QA; ' || sql
I am not sure whether snowflake.createStatement allows for a multi-statement query to be passed in, though. You'll have to let me know if that works.
I have a tSQLt test which I expect to fail but it runs with success. When I run the code outside the procedure, it fails as expected, but when executing the test with Run, no error occurs.
I have read the question tSQLt Testing SQL Server security permissions but the accepted answer does not solve my problem.
My test looks like this:
CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS
BEGIN
--EXEC tSQLt.ExpectException
EXECUTE AS USER = 'AFDK_Reader'
select *
from sys.user_token
SELECT * FROM fn_my_permissions('AWS', 'SCHEMA')
ORDER BY subentity_name, permission_name ;
SELECT *
FROM [AWS].[ADRESSEPUNKT_HISTORIK]
REVERT
END
The role has granted select permissions on the AFDK schema only and that is the only database role membership the SQL user has.
The AFDK_Reader has no permissions to read from the AWS schema.
Can anybody tell me how to get on with my debugging? Thanks in advance.
EXECUTE AS... REVERT commands don't behave in the way you are expecting inside a stored procedure. This is a general feature of stored procedure security in SQL Server; one common use of stored procedures is to abstract permissions. The MS docs page Customizing Permissions with Impersonation in SQL Server says:
SQL Server does not check the permissions of the caller if the stored
procedure and tables have the same owner.
and that's effectively what's happening here. Even though the EXECUTE AS changes the security context, that security context isn't checked inside the stored procedure.
The docs page also says:
However, ownership chaining doesn't work if objects have different
owners or in the case of dynamic SQL.
One way to get the behaviour you're expecting would be to run the SELECT statement from inside a dynamic SQL statement, which means that the active security context is tested against the table permissions:
CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS
BEGIN
EXEC tSQLt.ExpectException
EXECUTE AS USER = 'AFDK_Reader'
EXEC ('SELECT * FROM [AWS].[ADRESSEPUNKT_HISTORIK]')
REVERT
END
A alternative (better?) solution might be to use the built-in permission functions to test permissions settings through metadata. Here's one way, still using EXECUTE AS... REVERT and sys.fn_my_permission:
CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS
BEGIN
EXECUTE AS USER = 'AFDK_Reader'
DECLARE #permissionCount int = (SELECT COUNT(*) FROM sys.fn_my_permissions('[AWS].[ADRESSEPUNKT_HISTORIK]', 'OBJECT') WHERE permission_name = 'SELECT' AND subentity_name = '')
REVERT
EXEC tSQLt.AssertEquals 0, #permissionCount
END
I'm trying to create a stored procedure that creates a dB-snapshot for a non privileged user.
The idea is to provide to a normal user a way to create a dB snapshot in order to run queries against it and delete the snapshot when it is done with it.
I thought it would be possible to use the 'with execute as owner" in the procedure declaration. However, I always get the following error:
CREATE DATABASE permission denied in database 'master'.
Here is my code:
-- The user that create the sp has sysadmin right
CREATE OR ALTER PROCEDURE [dbo].[makeSnapshot] WITH EXECUTE AS OWNER
AS
-- just an extract of the code (should test if exist...)
DECLARE #exec NVARCHAR(2000)
set #exec = 'CREATE DATABASE test_dbss1900 ON ( NAME = test, FILENAME =
''C:\\Program Files\\Microsoft SQL Server\\MSSQL14.SQLSERVER2017\\MSSQL\\Data\\test_1900.ss'' ) AS SNAPSHOT OF test';
EXEC (#exec)
GO
-- try to execute it (with any user)
EXEC dbo.[makeSnapshot]
Has anyone an idea how I can come up with a stored proc that will allow a normal user to create a db snapshot?
Thank for any help!
José
I actually found a solution by looking at - http://www.sommarskog.se/grantperm.html#serverlevel (chapter 5.3)
The way was to use certificates
I want to know what are the default permissions for sql server stored procedures.
For example, I created a user in database but no role or no permissions granted except execute permission like:
GRANT EXECUTE ON SCHEMA :: [dbo] TO [newUser]
later on me (as a sa login) created a stored procedure that makes just a select from a table like:
CREATE PROCEDURE dbo.selectX AS
BEGIN
SELECT ID, NAME FROM MyTable
END
and my user [newUser] can execute and see the result of select statement like:
EXEC dbo.selectX
up until now, I can assume that stored procedures have default permissions that my user don't have.
I tried other dmls like UPDATE, INSERT, DELETE and user managed to execute procedures successfully.
But! when I created a proceure with ddl as create table .. such as:
CREATE PROCEDURE dbo.crtNT AS
BEGIN
CREATE TABLE del(id int) --for test
END
and when the newUser executes this procedure error occurs like:
CREATE TABLE permission denied in database 'MyDb'
And here is my question: By default, do stored procedures have permission with DML(select, insert, update vs.) but not with DDL(create, drop table vs.)? Because I didn't see any explanation about this situation in Books Online or anywhere else.
Any comments will be welcomed.
You are correct in your assumption that DDL statements are treated differently.
The procedures with DML statements work through the mechanism of Ownership Chaining however as this quote from the EXECUTE AS documentation states
Remember that ownership chaining applies only to DML statements.