I am currently migrating our cloud infrastructure over to Terraform; it's all gone well so far. Right now, though, I am trying to set up the SQL Server app, and the current setup, which I need to mirror to migrate, takes the existing resource group Object ID and appends it onto a unique string, for example, sqldatabase23456-resource group object id.
In arm this is done the following way:
"sqlServerAdministratorUsername": "[concat('l', uniqueString(resourceGroup().id))]",
Except I am making the resource group in Terraform, so the variable needs to use the meta argument depends on as the variable value can not exist before the resource group exists. I don't think this is possible when reading the depends on the material from Terraform; it only seems to work for resources, not other items. Link to document: https://www.terraform.io/docs/language/meta-arguments/depends_on.html
I have seen it slightly discussed on here:
Terraform, can a resource be passed as a variable into a module?
I am happy to build this SQL server as a module. I need this to work this way; otherwise, we won't migrate over to Terraform. As we can't change the current setup with SQL, too much depends on it.
I need to pass the values into the login part of SQL server code example below:
resource "azurerm_mssql_server" "example" {
name = "mssqlserver"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
version = "12.0"
administrator_login = var.resourcegroup_withuniquestring
administrator_login_password = var.resourcegroup_withuniquestring_password
minimum_tls_version = "1.2"
tags = {
environment = "production"
}
}
I don't really know what do you mean by variable in this context.
Terraform has locals. Locals can reference other Terraform resources, by this making the dependency implicit, so there is no need for depends_on. Example:
resource "random_string" "random" {
length = 16
special = true
override_special = "/#£$"
}
locals {
sql_server_administrator_name = join("", "sqldatabase", random_string.random.result, "-", azurerm_resource_group.example.name,
}
Locals can be referenced in other resources:
resource "azurerm_mssql_server" "example" {
name = "mssqlserver"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
version = "12.0"
administrator_login = local.sql_server_administrator_name
administrator_login_password = local.password
minimum_tls_version = "1.2"
}
I actually figured this out using the prompting of Ervins anwser.
I used locals to put the resource group id as a variable but the problem with that is it will output a huge string that can not be used for account names, so here is the following steps that I took, to trim it down and make it usable.
I first made three local variables one to trim down the string and get the ID number of the resource group and disregard everything else.
Another to output the whole string so I could work out the string value and how many characters to count in.
And a Third to put both the unique string and the Resource Group ID number on the end.
Below is the code to trim the string.
resource_group_id = substr(("${azurerm_resource_group.Example_Terraform_Testing.id}"),15,36)
This is using the function substr in Terraform which allows you to chop up strings, as most of the functions will only let you work with maps, or lists.
Link to substr: https://www.terraform.io/docs/language/functions/substr.html
To work out the exact number I first used format as a local value then outputed the value through an output variable like this:
resource_group_id_full_value = format("${azurerm_resource_group.Example_Terraform_Testing.id}")
output "resource_group_id__full_value" { value = local.resource_group_id_full_value }
This will then output the whole string like this when running Terraform Plan or Apply: resource_group_id__full_value = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/Example_Terraform_Testing"
To user substr you need to count the characters from the first / and the first / is 0 all / are a value. Unlike other languages that disregard special characters.
Once I had that as a local value I could then append that onto my local variable string like this:
sql_admin_login = format("${random_string.myrandom.id}-${local.resource_group_id}")
This will then give me the format I wanted like this, "qpncxt-00000000-0000-0000-0000-000000000000" and meet the account name requirements set by Azure, to be unique and to not have any special characters in and not to be too long. Also the great thing about substr is If I need the value to be shorter I just put in a lower number in the string.
Here is my full code to get this working:
locals {
# Ids for Resource Group, merged together with unique string
resource_group_id_full_value = format("${azurerm_resource_group.Example_In_The_Cloud_Terraform_Testing.id}")
resource_group_id = substr(("${azurerm_resource_group.Example_Terraform_Testing.id}"), 15, 36)
sql_admin_login = format("${random_string.myrandom.id}-${local.resource_group_id}")
sql_admin_password = format("${random_string.myrandom.id}-${local.resource_group_id}")
sql_server_name = format("sqlserver-${local.resource_group_id}")
}
resource "azurerm_mssql_server" "example_sql_server" {
name = local.sql_server_name
location = azurerm_resource_group.Example_Terraform_Testing.location
resource_group_name = azurerm_resource_group.Example_Terraform_Testing.name
version = "12.0"
administrator_login = local.sql_admin_login
administrator_login_password = local.sql_admin_password
minimum_tls_version = "1.2"
tags = {
environment = "production"
}
}
Links used to work this out:
Terraform Local Values: https://www.terraform.io/docs/language/values/locals.html
Terraform String Functions:
https://www.terraform.io/docs/language/functions/substr.html
Related
I have created one Snowflake storage integration as follow:
create or replace storage integration s3_int
type = external_stage
storage_provider = s3
enabled = TRUE
storage_aws_role_arn = 'arn:aws:iam::xxx'
storage_allowed_locations = ('S3://location1',
's3://location2',
's3://location4',
's3://location5',
's3://location6',
's3://location7',
's3://location8');
Now I like to add 2 more locations, I know that I can use SET storage_allowed_locations like following:
ALTER STORAGE INTEGRATION s3_int
SET storage_allowed_locations = ('S3://location1',
's3://location2',
's3://location4',
's3://location5',
's3://location6',
's3://location7',
's3://location8',
's3://location9',
's3://location10');
But the problem is, along the time I will have more and more locations to add in this storage_allowed_location list. Do I always have to give the whole list of locations and add the new locations in the end of it? It will become super long and annoying in the end. Do you know if there is other ways to add/append the new location in existing location list?
At this point, yes, the storage locations have to be added along with the existing ones.
In Script Component [Input0_ProcessInputRow], i am trying to fetch "ReadWrite" global variable value and it throws below error.
ERROR:
The collection of variables locked for read and write access is not available outside of PostExecute.
Below is my code
If Row.Column13 = "C" Then
Variables.mTotalCreditCount = Variables.mTotalCreditCount - 1
Variables.mTotalCreditAmount = Variables.mTotalCreditAmount - CDbl(Row.Column14)
ElseIf Row.Column13 = "D" Then
Variables.mTotalDebitCount = Variables.mTotalDebitCount - 1
Variables.mTotalDebitAmount = Variables.mTotalDebitAmount - CDbl(Row.Column14)
End If
I also tried to read the value in local variable and then assign to global variable in the PostExecute() as below. No Luck
If Row.Column13 = "C" Then
mTotalCrCnt = Variables.mTotalCreditCount - 1
mTotalCrAmt = Variables.mTotalCreditAmount - CDbl(Row.Column14)
ElseIf Row.Column13 = "D" Then
mTotalDbCnt = Variables.mTotalDebitCount
mTotalDbCnt = mTotalDbCnt - 1
mTotalDbAmt = Variables.mTotalDebitAmount
mTotalDbAmt = mTotalDbAmt - CDbl(Row.Column14)
End If
Public Overrides Sub PostExecute()
MyBase.PostExecute()
Variables.ProcessCount = intProcessCount
Variables.mTotalCreditCount = mTotalCrCnt
Variables.mTotalCreditAmount = mTotalCrAmt
Variables.mTotalDebitCount = mTotalDbCnt
Variables.mTotalDebitAmount = mTotalDbAmt
End Sub
Any assistance please?
Looking to your comment it looks that you have solved the issue, but i am posting this answer to give information about how working with variables in a SSIS script and how to solve a similar issue, so it can helps other users
SSIS Variables
Variables Stores values that can be used in all SSIS components and containers.
Integration Services supports two types of variables: user-defined variables and system variables. User-defined variables are defined by package developers, and system variables are defined by Integration Services. You can create as many user-defined variables as a package requires, but you cannot create additional system variables. More info in this MSDN article
Using Variables in Script components
Every script has a ReadOnlyVariables and ReadWriteVariables Lists that can be defined on the Script page.
ReadOnlyVariables
ReadOnlyVariables can be accessed from all script Sub's and they are Read-Only as they are named.
ReadWriteVariables
The collection of ReadWriteVariables is only available in the PostExecute method to maximize performance and minimize the risk of locking conflicts. Therefore you cannot directly increment the value of a package variable as you process each row of data. Increment the value of a local variable instead, and set the value of the package variable to the value of the local variable in the PostExecute method after all data has been processed. You can also use the VariableDispenser property to work around this limitation. However, writing directly to a package variable as each row is processed will negatively impact performance and increase the risk of locking conflicts. More in this MSDN article
Methods to Work with Variables
There are 3 Methods to work with variables:
Accessing them directly after having selected them as ReadOnlyVariables or ReadWriteVariables in the script page
Using a Variables Dispenser (LockForRead and LockForWrite methods)
IDTSVariables100 vars = null;
VariableDispenser.LockForRead("User::MyVariable");
VariableDispenser.GetVariables(out vars);
string TaskName = vars["User::MyVariable"].Value.ToString();
vars.Unlock();
Using SSIS Logging Task: to read variable and log them to Execution Log, Message Box or File
There are many articles Talking about this methods and you can refer to them to learn more
VariableDispenser.GetVariables Method (Variables)
VariableDispenser.LockForWrite Method (String)
3 Ways -SSIS Read Write Variables – Script Task C# / VB.net
Read and Write variables in a Script Component in SSIS (SQL Server Integration Services) using C#
Use SSIS Variables and Parameters in a Script Task
We are looking at syncing some of our LDAP (Active Directory) data with what is stored in SAP. SAP provides several function modules that allow you to write a custom program to handle mapping the data, but we are looking to use the provided solution that makes use of RSLDAPSYNC_USER.
The issue I'm having is understanding how the mapping of fields is performed in LDAPMAP. In particular, when performing the Mapping Overview, where are the structures as shown below defined?
Also, we have a function module that is currently available for grabbing all of the fields we would like to send to LDAP, but can the screen shown below be used to call a custom function module to grab the data I require? If so, then please give an example.
Thanks,
Mike
I am not sure if that is what you ask. As an answer to your second question:
You can give attributes that you want to get. The LDAP_READ function will return the results in entries parameter.
CALL FUNCTION 'LDAP_READ'
EXPORTING
base = base
* scope = 2
filter = filter
* attributes = attributes_ldap
timeout = s_timeout
attributes = t_attributes_ldap
IMPORTING
entries = t_entries_ldap "<< entries will come
EXCEPTIONS
no_authoriz = 1
conn_outdate = 2
ldap_failure = 3
not_alive = 4
other_error = 5
OTHERS = 6.
Entries parameter looks like:
Attributes parameter looks like:
I've been changing this call around for some time and can not quite get it to work. I am trying to return some records from a database and exclude items based on their ID numbers.
This is a database of quotes and when someone hides a quote it stores the quoteID in a cookie (required) and when returning to the site, The cookie.value is read.
I need to filter these IDs out of the return query and display the results in a view using WebGrid. I have the cookie part working. I retrieve the values (could be multiple) and split this into a list of strings. I then run a foreach loop ans parse the string to an integer and then try to remove quotes that I have retrieved from the database. Here is the code as I am using it...
// Filter on Cookie Value using tokenizer
string value = Request.Cookies.Get("hideCookie").Value;
List<string> values = value.Split(' ').ToList();
var quotes2 = db.Quotes.Include(q => q.QName);
foreach (var i in values)
{
int idv = int.Parse(i);
quotes2 = from q in quotes2 where q.QuoteID != idv select q;
}
return View(quotes2.ToList());
This throws an error "A specified Include path is not valid. The EntityType 'Exercise4.Models.Quote' does not declare a navigation property with the name 'QName'." (QName being an entry in the database record).
If I remove the ToList() in the return View(), quotes2 gets passed to the View but throws this error on the webGrid "A specified Include path is not valid. The EntityType 'Exercise4.Models.Quote' does not declare a navigation property with the name 'QName'."
I'm sure there is a much better way of doing this. Can you please point me in the correct direction so I can stop banging my head against my desk...
I would like to get all the files that a sub-folder holds in a string array.
So, I have tried something like the following:
var IOstore = IsolatedStorageFile.GetUserStoreForApplication();
string searchpath = System.IO.Path.Combine("product", ProductName);
string filesInSubDirs[] = IOstore.GetFileNames(searchpath);
But I got all the files in the "product" folder. I have also tried with "productname" only as the parameter.
Thanks for your help.
The search pattern for a sub-folder needs to include "*.*" at the end to pattern match any file, which would make your code something like the following:
var IOstore = IsolatedStorageFile.GetUserStoreForApplication();
string searchpath = System.IO.Path.Combine("product", ProductName);
searchpath = string.Format("{0}\\*.*", searchpath);
string filesInSubDirs[] = IOstore.GetFileNames(searchpath);
Something you might want to try. (this is sort of a left field answer, sorry). In my dropbox client http://sharpdropbox.codeplex.com/) I have a set of facades for System.IO.File, System.IO.FileInfo, System.IO.Directory, and System.IO.DirectoryInfo. They work pretty good and I have tested them.
Basically, you add a Using or Import for System.IO.IsolatedStorage and then PSFile, PSDirectory, PSFileInfo, or PSDirectoryInfo. It's saved me from having to remember all the nuances... for instance if you are querying a directory, it knows to add a slash, etc. BTW, the "PS" prefix stands for "Persisted Storage" which is what IsolatedStorage is sometimes called (starting them with an "I" implies they are interfaces.. and having no prefix makes things even more confusing).
Anyway, you can grab the code from source or I believe the last release had the DLLs for them (it's called something like "IsolatedStorageFacade-WP7")