diff --git a/tutorials/1-0-list-env-instance.tf b/tutorials/1-0-list-env-instance.tf new file mode 100644 index 0000000..9241a1d --- /dev/null +++ b/tutorials/1-0-list-env-instance.tf @@ -0,0 +1,13 @@ +# List all environments using settings +data "bytebase_setting" "environments" { + name = "settings/ENVIRONMENT" +} +output "all_environments" { + value = data.bytebase_setting.environments +} + +# List all instances +data "bytebase_instance_list" "all" {} +output "all_instances" { + value = data.bytebase_instance_list.all +} \ No newline at end of file diff --git a/tutorials/1-1-env-alt.tf.alt b/tutorials/1-1-env-alt.tf.alt new file mode 100644 index 0000000..2f284ad --- /dev/null +++ b/tutorials/1-1-env-alt.tf.alt @@ -0,0 +1,17 @@ +resource "bytebase_environment" "test" { + resource_id = "test" + title = "Test" + order = 0 + protected = false +} + +# Create Production environment +# depends_on ensures environments are created in sequence +# This prevents API conflicts when updating the environment list +resource "bytebase_environment" "prod" { + depends_on = [bytebase_environment.test] + resource_id = "prod" + title = "Prod" + order = 1 + protected = true +} \ No newline at end of file diff --git a/tutorials/1-1-env.tf b/tutorials/1-1-env.tf new file mode 100644 index 0000000..f651686 --- /dev/null +++ b/tutorials/1-1-env.tf @@ -0,0 +1,17 @@ +# Environment Settings +resource "bytebase_setting" "environments" { + name = "settings/ENVIRONMENT" + + environment_setting { + environment { + id = "test" + title = "Test" + protected = false + } + environment { + id = "prod" + title = "Prod" + protected = true + } + } +} \ No newline at end of file diff --git a/tutorials/1-instances-alt.tf.alt b/tutorials/1-instances-alt.tf.alt new file mode 100644 index 0000000..ae1362a --- /dev/null +++ b/tutorials/1-instances-alt.tf.alt @@ -0,0 +1,37 @@ +# Built-in Test Instance +resource "bytebase_instance" "test" { + depends_on = [bytebase_environment.test] + resource_id = "test-sample-instance" + environment = bytebase_environment.test.name + title = "Test Sample Instance" + engine = "POSTGRES" + activation = true + + data_sources { + id = "admin data source test-sample-instance" + type = "ADMIN" + host = "/tmp" + port = "8083" + username = "bbsample" + password = "" + } +} + +# Built-in Prod Instance +resource "bytebase_instance" "prod" { + depends_on = [bytebase_environment.prod] + resource_id = "prod-sample-instance" + environment = bytebase_environment.prod.name + title = "Prod Sample Instance" + engine = "POSTGRES" + activation = true + + data_sources { + id = "admin data source prod-sample-instance" + type = "ADMIN" + host = "/tmp" + port = "8084" + username = "bbsample" + password = "" + } +} \ No newline at end of file diff --git a/tutorials/3-1-workspace-profile.tf b/tutorials/3-1-workspace-profile.tf new file mode 100644 index 0000000..b7800c3 --- /dev/null +++ b/tutorials/3-1-workspace-profile.tf @@ -0,0 +1,10 @@ +resource "bytebase_setting" "workspace_profile" { + name = "settings/WORKSPACE_PROFILE" + + workspace_profile { + disallow_signup = true + domains = ["example.com"] + enforce_identity_domain = false + external_url = "https://valid-just-tadpole.ngrok-free.app" + } +} \ No newline at end of file diff --git a/tutorials/3-2-env-setting.tf b/tutorials/3-2-env-setting.tf new file mode 100644 index 0000000..1ca4398 --- /dev/null +++ b/tutorials/3-2-env-setting.tf @@ -0,0 +1,53 @@ +resource "bytebase_policy" "rollout_policy_test" { + depends_on = [bytebase_setting.environments] + parent = bytebase_setting.environments.environment_setting[0].environment[0].name + type = "ROLLOUT_POLICY" + + rollout_policy { + automatic = true + roles = [ + "roles/workspaceAdmin", + "roles/projectOwner", + "roles/LAST_APPROVER", + "roles/CREATOR" + ] + } +} + +resource "bytebase_policy" "rollout_policy_prod" { + depends_on = [bytebase_setting.environments] + parent = bytebase_setting.environments.environment_setting[0].environment[1].name + type = "ROLLOUT_POLICY" + + rollout_policy { + automatic = false + roles = [ + "roles/workspaceAdmin", + "roles/projectOwner", + "roles/LAST_APPROVER", + "roles/CREATOR" + ] + } +} + +resource "bytebase_policy" "disable_copy_data_policy_prod" { + depends_on = [bytebase_setting.environments] + parent = bytebase_setting.environments.environment_setting[0].environment[1].name + type = "DISABLE_COPY_DATA" + + disable_copy_data_policy { + enable = true + } +} + +resource "bytebase_policy" "data_source_query_policy_prod" { + depends_on = [bytebase_setting.environments] + parent = bytebase_setting.environments.environment_setting[0].environment[1].name + type = "DATA_SOURCE_QUERY" + + data_source_query_policy { + restriction = "FALLBACK" # or DISALLOW or RESTRICTION_UNSPECIFIED + disallow_ddl = true + disallow_dml = true + } +} \ No newline at end of file diff --git a/tutorials/3-3-risk.tf b/tutorials/3-3-risk.tf new file mode 100644 index 0000000..e02d402 --- /dev/null +++ b/tutorials/3-3-risk.tf @@ -0,0 +1,15 @@ +resource "bytebase_risk" "dml_moderate" { + title = "DML Moderate Risk" + source = "DML" + level = 200 + active = true + condition = "environment_id == \"prod\" && affected_rows >= 100" +} + +resource "bytebase_risk" "ddl_high" { + title = "DDL High Risk" + source = "DDL" + level = 300 + active = true + condition = "environment_id == \"prod\"" +} \ No newline at end of file diff --git a/tutorials/3-4-approval-flow.tf b/tutorials/3-4-approval-flow.tf new file mode 100644 index 0000000..8af617f --- /dev/null +++ b/tutorials/3-4-approval-flow.tf @@ -0,0 +1,24 @@ +resource "bytebase_setting" "approval_flow" { + name = "settings/WORKSPACE_APPROVAL" + + approval_flow { + rules { + flow { + title = "Project Owner → DBA → Admin" + description = "Need DBA and workspace admin approval" + + steps { role = "roles/projectOwner" } + steps { role = "roles/workspaceDBA" } + steps { role = "roles/workspaceAdmin" } + } + conditions { + source = "DML" + level = "MODERATE" + } + conditions { + source = "DDL" + level = "HIGH" + } + } + } +} \ No newline at end of file diff --git a/tutorials/4-sql-review.tf b/tutorials/4-sql-review.tf new file mode 100644 index 0000000..2a7350b --- /dev/null +++ b/tutorials/4-sql-review.tf @@ -0,0 +1,40 @@ +resource "bytebase_review_config" "sample" { + depends_on = [ + bytebase_setting.environments + ] + + resource_id = "review-config-sample" + title = "Sample SQL Review Config" + enabled = true + resources = toset([ + bytebase_setting.environments.environment_setting[0].environment[1].name + ]) + rules { + type = "column.no-null" + engine = "POSTGRES" + level = "WARNING" + } + rules { + type = "column.required" + engine = "POSTGRES" + level = "ERROR" + payload = "{\"list\":[\"id\",\"created_ts\",\"updated_ts\",\"creator_id\",\"updater_id\"]}" + } + rules { + type = "table.require-pk" + engine = "POSTGRES" + level = "ERROR" + } + rules { + type = "naming.column" + engine = "POSTGRES" + level = "ERROR" + payload = "{\"format\":\"^[a-z]+(_[a-z]+)*$\",\"maxLength\":64}" + } + rules { + type = "statement.maximum-limit-value" + engine = "POSTGRES" + level = "ERROR" + payload = "{\"number\":1000}" + } +} \ No newline at end of file diff --git a/tutorials/5-user-iam.tf b/tutorials/5-user-iam.tf new file mode 100644 index 0000000..0c1aa90 --- /dev/null +++ b/tutorials/5-user-iam.tf @@ -0,0 +1,184 @@ +# Create users and groups +resource "bytebase_user" "workspace_admin" { + email = "admin@example.com" + title = "Workspace Admin" + type = "USER" +} + +resource "bytebase_user" "tf_service_account" { + email = "tf@service.bytebase.com" + title = "Terraform Service Account" + type = "SERVICE_ACCOUNT" +} + +resource "bytebase_user" "workspace_dba1" { + email = "dba@example.com" + title = "Database Administrator 1" + type = "USER" +} + +resource "bytebase_user" "workspace_dba2" { + email = "dba2@example.com" + title = "Database Administrator 2" + type = "USER" +} + +resource "bytebase_user" "dev1" { + email = "dev1@example.com" + title = "Developer 1" + type = "USER" +} + +resource "bytebase_user" "dev2" { + email = "dev2@example.com" + title = "Developer 2" + type = "USER" +} + +resource "bytebase_user" "dev3" { + email = "dev3@example.com" + title = "Developer 3" + type = "USER" +} + +resource "bytebase_user" "qa1" { + email = "qa1@example.com" + title = "QA Tester 1" + type = "USER" +} + +resource "bytebase_user" "qa2" { + email = "qa2@example.com" + title = "QA Tester 2" + type = "USER" +} + +# Create groups +resource "bytebase_group" "developers" { + email = "developers@example.com" + title = "Developer Team" + description = "Group for all developers" + + members { + member = "users/${bytebase_user.dev1.email}" + role = "OWNER" + } + + members { + member = "users/${bytebase_user.dev2.email}" + role = "MEMBER" + } + + members { + member = "users/${bytebase_user.dev3.email}" + role = "MEMBER" + } +} + +resource "bytebase_group" "qa" { + email = "qa@example.com" + title = "QA Team" + description = "Group for all QA testers" + + members { + member = "users/${bytebase_user.qa1.email}" + role = "OWNER" + } + + members { + member = "users/${bytebase_user.qa2.email}" + role = "MEMBER" + } +} + +resource "bytebase_iam_policy" "workspace_iam" { + depends_on = [ + bytebase_user.workspace_admin, + bytebase_user.tf_service_account, + bytebase_user.workspace_dba1, + bytebase_user.workspace_dba2, + bytebase_group.qa + ] + + parent = "workspaces/-" + + iam_policy { + + binding { + role = "roles/workspaceAdmin" + members = [ + format("user:%s", bytebase_user.workspace_admin.email), + format("user:%s", bytebase_user.tf_service_account.email), + ] + } + + binding { + role = "roles/workspaceDBA" + members = [ + format("user:%s", bytebase_user.workspace_dba1.email), + format("user:%s", bytebase_user.workspace_dba2.email) + ] + } + + binding { + role = "roles/workspaceMember" + members = [ + format("user:%s", bytebase_user.dev1.email), + format("user:%s", bytebase_user.dev2.email), + format("user:%s", bytebase_user.dev3.email), + format("user:%s", bytebase_user.qa1.email), + format("user:%s", bytebase_user.qa2.email) + ] + } + + binding { + role = "roles/projectViewer" + members = [ + format("group:%s", bytebase_group.qa.email), + ] + } + } +} + +resource "bytebase_iam_policy" "project_iam" { + depends_on = [ + bytebase_group.developers, + bytebase_user.workspace_dba1, + bytebase_user.workspace_dba2 + ] + + parent = bytebase_project.project-two.name + + iam_policy { + + binding { + role = "roles/projectOwner" + members = [ + format("user:%s", bytebase_user.workspace_dba1.email), + format("user:%s", bytebase_user.workspace_dba2.email) + ] + } + + binding { + role = "roles/projectDeveloper" + members = [ + "allUsers", + format("group:%s", bytebase_group.developers.email) + ] + } + + binding { + role = "roles/sqlEditorUser" + members = [ + format("group:%s", bytebase_group.developers.email) + ] + condition { + database = "instances/prod-sample-instance/databases/hr_prod" + schema = "public" + tables = ["employee","department"] + expire_timestamp = "2027-07-10T16:17:49Z" + } + } + + } +} \ No newline at end of file diff --git a/tutorials/6-1-semantic-types.tf b/tutorials/6-1-semantic-types.tf new file mode 100644 index 0000000..b91d560 --- /dev/null +++ b/tutorials/6-1-semantic-types.tf @@ -0,0 +1,40 @@ +resource "bytebase_setting" "semantic_types" { + name = "settings/SEMANTIC_TYPES" + + semantic_types { + id = "full-mask" + title = "Full mask" + algorithm { + full_mask { + substitution = "***" + } + } + } + + semantic_types { + id = "date-year-mask" + title = "Date year mask" + algorithm { + range_mask { + slices { + start = 0 + end = 4 + substitution = "****" + } + } + } + } + + semantic_types { + id = "name-first-letter-only" + title = "Name first letter only" + algorithm { + inner_outer_mask { + prefix_len = 1 + suffix_len = 0 + substitution = "*" + type = "INNER" + } + } + } +} \ No newline at end of file diff --git a/tutorials/6-2-classification.tf b/tutorials/6-2-classification.tf new file mode 100644 index 0000000..d687cbd --- /dev/null +++ b/tutorials/6-2-classification.tf @@ -0,0 +1,45 @@ +resource "bytebase_setting" "classification" { + name = "settings/DATA_CLASSIFICATION" + + classification { + id = "classification-example" + title = "Classification Example" + + levels { + id = "1" + title = "Level 1" + } + levels { + id = "2" + title = "Level 2" + } + + classifications { + id = "1" + title = "Basic" + } + + classifications { + id = "1-1" + title = "User basic" + level = "1" + } + + classifications { + id = "1-2" + title = "User contact info" + level = "2" + } + + classifications { + id = "2" + title = "Employment" + } + + classifications { + id = "2-1" + title = "Employment info" + level = "2" + } + } +} \ No newline at end of file diff --git a/tutorials/6-3-global-data-masking.tf b/tutorials/6-3-global-data-masking.tf new file mode 100644 index 0000000..54031a4 --- /dev/null +++ b/tutorials/6-3-global-data-masking.tf @@ -0,0 +1,32 @@ +resource "bytebase_policy" "global_masking_policy" { + depends_on = [ + bytebase_instance.prod, + bytebase_setting.environments + ] + + parent = "workspaces/-" + type = "MASKING_RULE" + enforce = true + inherit_from_parent = false + + global_masking_policy { + + rules { + condition = "column_name == \"birth_date\"" + id = "birth-date-mask" + semantic_type = "date-year-mask" + } + + rules { + condition = "column_name == \"last_name\"" + id = "last-name-first-letter-only" + semantic_type = "name-first-letter-only" + } + + rules { + condition = "classification_level in [\"2\"]" + id = "classification-level-2" + semantic_type = "full-mask" + } + } +} \ No newline at end of file diff --git a/tutorials/6-4-database-masking.tf b/tutorials/6-4-database-masking.tf new file mode 100644 index 0000000..4d646ae --- /dev/null +++ b/tutorials/6-4-database-masking.tf @@ -0,0 +1,28 @@ +resource "bytebase_database" "database" { + depends_on = [ + bytebase_instance.prod, + bytebase_project.project-two, + bytebase_setting.environments + ] + + name = "instances/prod-sample-instance/databases/hr_prod" + project = bytebase_project.project-two.name + environment = bytebase_setting.environments.environment_setting[0].environment[1].name + + catalog { + schemas { + name = "public" + tables { + name = "salary" + columns { + name = "from_date" + semantic_type = "date-year-mask" + } + columns { + name = "amount" + classification = "2-1" + } + } + } + } +} \ No newline at end of file diff --git a/tutorials/6-5-masking-exception.tf b/tutorials/6-5-masking-exception.tf new file mode 100644 index 0000000..20803ae --- /dev/null +++ b/tutorials/6-5-masking-exception.tf @@ -0,0 +1,28 @@ +resource "bytebase_policy" "masking_exception_policy" { + depends_on = [ + bytebase_project.project-two, + bytebase_instance.prod + ] + + parent = bytebase_project.project-two.name + type = "MASKING_EXCEPTION" + enforce = true + inherit_from_parent = false + + masking_exception_policy { + exceptions { + database = "instances/prod-sample-instance/databases/hr_prod" + table = "employee" + column = "birth_date" + member = "user:admin@example.com" + action = "QUERY" + } + exceptions { + database = "instances/prod-sample-instance/databases/hr_prod" + table = "employee" + column = "last_name" + member = "user:admin@example.com" + action = "EXPORT" + } + } +} \ No newline at end of file