I am using Terraform to build an Azure DB and set the correct Azure AD Admin etc - all working well.
I now need to create
CREATE LOGIN [XXX-XXX] FROM EXTERNAL PROVIDER;
CREATE USER [XXX-XXX] FOR LOGIN [XXX-XXX];
ALTER ROLE db_datareader ADD MEMBER [XXX-XXX]
Any ideas if this is possible within Terraform - thinking its the easiest way as the user is already authorised to create the database.
Its not possible to directly run the commands that you have mentioned in the question but you can use Invoke-sqlcmd and authenticate with your AAD admin credentials and run the commands .
I tested the scenario with the below code :
provider "azurerm" {
features{}
}
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_sql_server" "example" {
name = "ansumansqlserver"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
version = "12.0"
administrator_login = "admin"
administrator_login_password = "password"
tags = {
environment = "production"
}
}
resource "azurerm_storage_account" "example" {
name = "ansumansacc"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_sql_database" "example" {
name = "ansumansqldatabase"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
server_name = azurerm_sql_server.example.name
extended_auditing_policy {
storage_endpoint = azurerm_storage_account.example.primary_blob_endpoint
storage_account_access_key = azurerm_storage_account.example.primary_access_key
storage_account_access_key_is_secondary = true
retention_in_days = 6
}
tags = {
environment = "production"
}
}
resource "azurerm_sql_active_directory_administrator" "example" {
server_name = azurerm_sql_server.example.name
resource_group_name = azurerm_resource_group.example.name
login = "sqladmin"
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
}
## creating Login in master database first
resource "null_resource" "master"{
provisioner "local-exec"{
command = <<EOT
Set-AzContext -SubscriptionId "<SubscriptionID>"
$token = (Get-AzAccessToken -ResourceUrl https://database.windows.net).Token
Invoke-SqlCmd -ServerInstance ${azurerm_sql_server.example.fully_qualified_domain_name} -Database master -AccessToken $token -Query "CREATE LOGIN [user#tenantname.onmicrosoft.com] FROM EXTERNAL PROVIDER"
EOT
interpreter = ["PowerShell", "-Command"]
}
depends_on=[
azurerm_sql_active_directory_administrator.example,
azurerm_sql_database.example
]
}
## creating the user from the login created in master and assigning role
resource "null_resource" "database"{
provisioner "local-exec"{
command = <<EOT
Set-AzContext -SubscriptionId "<SubscriptionID>"
$token = (Get-AzAccessToken -ResourceUrl https://database.windows.net).Token
$query= #'
CREATE USER [AJAY] FOR LOGIN [user#tenantname.onmicrosoft.com];
GO
ALTER ROLE [db_datareader] ADD MEMBER [AJAY];
GO
'#
Invoke-SqlCmd -ServerInstance ${azurerm_sql_server.example.fully_qualified_domain_name} -Database ${azurerm_sql_database.example.name} -AccessToken $token -Query $query
EOT
interpreter = ["PowerShell", "-Command"]
}
depends_on = [
null_resource.master
]
}
Output:
Note: Please make sure to have Azure Powershell Module and SQLServer Powershell Module.
Related
I am trying to assign random passwords to multiple AAD users -in a csv file- with Terraform and resources "azuread_user"
First of all, I have this CSV file with some users:
user_name
User1
User2
User3
User4
Following, I read this CSV file using:
locals {
users = csvdecode(file("${path.module}/users.csv"))
}
Then, using "random_password" resource, I am generating a new password:
resource "random_password" "password" {
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
Next, with "azuread_user" I am trying to create the user with the password generated:
resource "azuread_user" "users" {
for_each = { for user in local.users : user.first_name => user }
user_principal_name = format(
"%s#%s",
each.value.user_name,
"mydomain.com"
)
password = each.value.password
display_name = "${each.value.first_name} ${each.value.last_name}"
}
but the problem is that every user has the same password from resource "random_password" "password".
How can I assign a randomly password for each user?
I tried to create users with random passwords as below:
locals {
users = {
"divv#xxxxxxx.onmicrosoft.com" = { first_name = "John", last_name = "Doe" , department = "Marketing Department" },
"shrav#xxxxxxxxxx.onmicrosoft.com" = { first_name = "Jane", last_name = "Doe" , department = "IT Department"}
}
}
resource "random_password" "passwords" {
for_each = local.users
length = 16
special = true
}
resource "azuread_user" "users" {
for_each = local.users
display_name = "${each.value.first_name} ${each.value.last_name}"
mail_nickname = each.value.first_name
user_principal_name = each.key
password = random_password.passwords[each.key].result
department = each.value.department
}
In order to check if random passwords are generated I stored them in keyvault and checked .
They seem to be different for different user.
resource "azurerm_key_vault" "example" {
name = "kavyaexmplekeyvault"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
enabled_for_disk_encryption = true
tenant_id = data.azurerm_client_config.current.tenant_id
soft_delete_retention_days = 7
purge_protection_enabled = false
sku_name = "standard"
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"Create",
"Get",
]
secret_permissions = [
"Set",
"Get",
"Delete",
"Purge",
"Recover",
"List"
]
storage_permissions = [
"Get","Set"
]
}
}
resource "azurerm_key_vault_secret" "password_one" {
for_each = local.users
name = "passwrdone${each.value.first_name}"
value = random_password.passwords[each.key].result
key_vault_id = azurerm_key_vault.example.id
}
Password for jane:
Password for john:
I am trying to use terraform to create adf linked services however the terraform resource doesn't give the option to select an already existing managed private endpoint for the linked service to communicate over but when creating from the portal, this is possible. bellow is my code
resource "azurerm_data_factory" "process-adf" {
resource_group_name = module.resourcegroup.resource_group.name
location = module.resourcegroup.resource_group.location
name = "adf"
managed_virtual_network_enabled = true
public_network_enabled = false
tags = var.tags
identity {
type = "SystemAssigned"
}
}
resource "azurerm_data_factory_linked_service_azure_sql_database" "process-mssql-adf" {
name = "mssql-adf"
data_factory_id = azurerm_data_factory.process-adf.id
integration_runtime_name = azurerm_data_factory_integration_runtime_azure.adf.id
connection_string = "data source=servername;initial catalog=databasename;user id=admin;Password=password;integrated security=True;encrypt=True;connection timeout=30"
}
resource "azurerm_data_factory_managed_private_endpoint" "adf-msssql-pe" {
name = "adf"
data_factory_id = azurerm_data_factory.process-adf.id
target_resource_id = azurerm_mssql_server.process-control.id
subresource_name = "sqlServer"
}
resource "azurerm_data_factory_integration_runtime_azure" "adf" {
name = "adf"
data_factory_id = azurerm_data_factory.process-adf.id
location = module.resourcegroup.resource_group.location
virtual_network_enabled = true
}
how do i point the resource azurerm_data_factory_linked_service_azure_sql_database to the resource azurerm_data_factory_managed_private_endpoint ?
Just having this input in my module
databases = [
{
db_name = "test_0"
db_owner = "testu_user_0",
extensions = ["unaccent"]
},
{
db_name = "test_db"
db_owner = "test_user"
extensions = ["uuid_ossp","pg_trgm"]
}
]
And then i need to loop through and make specifiec extensions. How can i achieve that?
For the database creation it was pretty straightforward
resource "postgresql_database" "db" {
for_each = {for db in var.databases : db.db_name => db}
name = each.key
owner = postgresql_role.specific_role["${each.value.db_owner}"].name
lifecycle {
prevent_destroy = false
}
}
But now when it comes to extensions im having a hard time to make it happen. I can see examples online, but they all use object with an array, not an array filled with objects and plus nested array inside.
# resource "postgresql_extension" "uuid_ossp" {
# for_each = {for db in var.databases : db.db_name => db}
# name = "uuid-ossp"
# database = each.key
# }
Please help
You have to flatten your data structure, in locals for instance, and then use that in postgresql_extension:
locals {
db_extentions = merge([
for db in var.databases :
{
for ext in db.extensions:
"${db.db_name}-${ext}" => {
db_name = db.db_name
db_owner = db.db_owner
extension = ext
}
}
]...) # <-- the dots are important! Don't remove them
}
resource "postgresql_extension" "uuid_ossp" {
for_each = local.db_extentions
name = each.value.extension
database = each.value.db_name
}
The three dots are for Expanding Function Arguments.
I created RDS instance using aws_db_instance (main.tf):
resource "aws_db_instance" "default" {
identifier = "${module.config.database["db_inst_name"]}"
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t3.micro"
name = "${module.config.database["db_name_prefix"]}${terraform.workspace}"
username = "${module.config.database["db_username"]}"
password = "${module.config.database["db_password"]}"
parameter_group_name = "default.mysql5.7"
skip_final_snapshot = true
}
Can I also create database schemas from file schema.sql with terraform apply?
$ tree -L 1
.
├── main.tf
└── schema.sql
You can use a provisioner (https://www.terraform.io/docs/provisioners/index.html) for that:
resource "aws_db_instance" "default" {
identifier = module.config.database["db_inst_name"]
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t3.micro"
name = "${module.config.database["db_name_prefix"]}${terraform.workspace}"
username = module.config.database["db_username"]
password = module.config.database["db_password"]
parameter_group_name = "default.mysql5.7"
skip_final_snapshot = true
provisioner "local-exec" {
command = "mysql --host=${self.address} --port=${self.port} --user=${self.username} --password=${self.password} < ./schema.sql"
}
}
#Apply scheme by using bastion host
resource "aws_db_instance" "default_bastion" {
identifier = module.config.database["db_inst_name"]
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t3.micro"
name = "${module.config.database["db_name_prefix"]}${terraform.workspace}"
username = module.config.database["db_username"]
password = module.config.database["db_password"]
parameter_group_name = "default.mysql5.7"
skip_final_snapshot = true
provisioner "file" {
connection {
user = "ec2-user"
host = "bastion.example.com"
private_key = file("~/.ssh/ec2_cert.pem")
}
source = "./schema.sql"
destination = "~"
}
provisioner "remote-exec" {
connection {
user = "ec2-user"
host = "bastion.example.com"
private_key = file("~/.ssh/ec2_cert.pem")
}
command = "mysql --host=${self.address} --port=${self.port} --user=${self.username} --password=${self.password} < ~/schema.sql"
}
}
mysql client needs to be installed on your device.
If you don't have direct access to your DB, there is also a remote-exec provisioner, where you can use a bastion host (transfer file to remote place with file provisioner first).
If your schema is not to complex, you could also use the MySQL provider of terraform:
https://www.terraform.io/docs/providers/mysql/index.html
To establish an RPC connection in the community edition we need to specify the rpc username, password and permissions but when we are integrating external database like MySQL and change the datasource type from INMEMORY to "DB" it does not allows to give user properties.
these are the settings I am using in my node.conf
security = {
authService = {
dataSource = {
type = "DB"
passwordEncryption = "SHIRO_1_CRYPT"
connection = {
jdbcUrl = "jdbc:mysql://localhost:3306"
username = "root"
password = "password"
driverClassName = "com.mysql.jdbc.Driver"
}
}
options = {
cache = {
expireAfterSecs = 120
maxEntries = 10000
}
}
}
Maybe I didn't understand your question, but database setup in node.conf is separate from RPC user setup in node.conf:
Database (PostGres in my case)
extraConfig = [
'dataSourceProperties.dataSourceClassName' : 'org.postgresql.ds.PGSimpleDataSource',
'dataSourceProperties.dataSource.url' : 'jdbc:postgresql://localhost:5432/postgres',
'dataSourceProperties.dataSource.user' : 'db_user',
'dataSourceProperties.dataSource.password' : 'db_user_password',
'database.transactionIsolationLevel' : 'READ_COMMITTED',
'database.initialiseSchema' : 'true'
]
RPC User
rpcUsers = [[ user: "rpc_user", "password": "rpc_user_password", "permissions": ["ALL"]]]
Ok, I'm adding my node's node.config (it's part of Corda TestNet, and it's deployed on Google Cloud):
baseDirectory = "."
compatibilityZoneURL = "https://netmap.testnet.r3.com"
emailAddress = "xxx"
jarDirs = [ "plugins", "cordapps" ]
sshd { port = 2222 }
myLegalName = "OU=xxx, O=TESTNET_xxx, L=London, C=GB"
keyStorePassword = "xxx"
trustStorePassword = "xxx"
crlCheckSoftFail = true
database = {
transactionIsolationLevel = "READ_COMMITTED"
initialiseSchema = "true"
}
dataSourceProperties {
dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
dataSource.url = "jdbc:postgresql://xxx:xxx/postgres"
dataSource.user = xxx
dataSource.password = xxx
}
p2pAddress = "xxx:xxx"
rpcSettings {
useSsl = false
standAloneBroker = false
address = "0.0.0.0:xxx"
adminAddress = "0.0.0.0:xxx"
}
rpcUsers = [
{ username=cordazoneservice, password=xxx, permissions=[ ALL ] }
]
devMode = false
cordappSignerKeyFingerprintBlacklist = []
useTestClock = false