Using Terraform to add Storage Virtual Network Rule - azure-storage

Is there a way to add a Virtual Network Rule to a storage account like there is with Azure SQL? There is the azurerm_sql_virtual_network_rule but there does not appear to be an equivalent for storage accounts.

Here is a sample of Usage with Network Rules, you could refer to it.
resource "azurerm_resource_group" "testrg" {
name = "resourceGroupName"
location = "westus"
}
resource "azurerm_virtual_network" "test" {
name = "virtnetname"
address_space = ["10.0.0.0/16"]
location = "${azurerm_resource_group.testrg.location}"
resource_group_name = "${azurerm_resource_group.testrg.name}"
}
resource "azurerm_subnet" "test" {
name = "subnetname"
resource_group_name = "${azurerm_resource_group.testrg.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefix = "10.0.2.0/24"
service_endpoints = ["Microsoft.Sql","Microsoft.Storage"]
}
resource "azurerm_storage_account" "testsa" {
name = "storageaccountname"
resource_group_name = "${azurerm_resource_group.testrg.name}"
location = "${azurerm_resource_group.testrg.location}"
account_tier = "Standard"
account_replication_type = "LRS"
network_rules {
ip_rules = ["127.0.0.1"]
virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"]
}
tags {
environment = "staging"
}
}

Related

Terraform Conditional Content Block Inside Resource

How do I make ip_configuration optional to turn the below:
resource "azurerm_firewall" "example" {
name = "testfirewall"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "configuration"
subnet_id = azurerm_subnet.example.id
public_ip_address_id = azurerm_public_ip.example.id
}
}
In to something that OPTIONALLY accepts values in this:
variable "ip_configuration" {
type = object({
name = string // Specifies the name of the IP Configuration.
subnet_id = string // Reference to the subnet associated with the IP Configuration.
public_ip_address_id = string // The ID of the Public IP Address associated with the firewall.
})
description = "(Optional) An ip_configuration block as documented"
default = null
}
I was looking at dynamic blocks, lookups and try expressions but nothing seems to work. Can anyone help? I have spent days on it trying to figure it out
Edit:
There maybe a neater way to do it, but I have found something that works. If someone can improve on this that would be great, but thanks for those who have replied.
subnet_id should only appear in the first ip_configuration which is why I have decided to use the numbering system on the key.
resource "azurerm_firewall" "example" {
name = "testfirewall"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dynamic "ip_configuration" {
for_each = var.ip_configuration
iterator = ip
content {
name = ip.value["name"]
subnet_id = ip.key == "0" ? ip.value["subnet_id"] : null
public_ip_address_id = ip.value["public_ip_address_id"]
}
}
}
variable "ip_configuration" {
type = map
description = <<-EOT
(Optional) An ip_configuration block is nested maps with 0, 1, 2, 3 as the name of the map as documented below:
name = string // Specifies the name of the IP Configuration.
public_ip_address_id = string // The ID of the Public IP Address associated with the firewall.
subnet_id = string // Reference to the subnet associated with the IP Configuration. The Subnet used for the Firewall must have the name AzureFirewallSubnet and the subnet mask must be at least a /26.
NOTE: Only the first map (with a key of 0) should have a subnet_id property.
EXAMPLE LAYOUT:
{
"0" = {
name = "first_pip_configuration"
public_ip_address_id = azurerm_public_ip.firstpip.id
subnet_id = azurerm_subnet.example.id
},
"1" = {
name = "second_pip_configuration"
public_ip_address_id = azurerm_public_ip.secondpip.id
},
"2" = {
name = "third_pip_configuration"
public_ip_address_id = azurerm_public_ip.thirdpip.id
}
}
EOT
default = {}
}
There maybe a neater way to do it, but I have found something that works. If someone can improve on this that would be great, but thanks for those who have replied.
subnet_id should only appear in the first ip_configuration which is why I have decided to use the numbering system on the key.
resource "azurerm_firewall" "example" {
name = "testfirewall"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dynamic "ip_configuration" {
for_each = var.ip_configuration
iterator = ip
content {
name = ip.value["name"]
subnet_id = ip.key == "0" ? ip.value["subnet_id"] : null
public_ip_address_id = ip.value["public_ip_address_id"]
}
}
}
variable "ip_configuration" {
type = map
description = <<-EOT
(Optional) An ip_configuration block is nested maps with 0, 1, 2, 3 as the name of the map as documented below:
name = string // Specifies the name of the IP Configuration.
public_ip_address_id = string // The ID of the Public IP Address associated with the firewall.
subnet_id = string // Reference to the subnet associated with the IP Configuration. The Subnet used for the Firewall must have the name AzureFirewallSubnet and the subnet mask must be at least a /26.
NOTE: Only the first map (with a key of 0) should have a subnet_id property.
EXAMPLE LAYOUT:
{
"0" = {
name = "first_pip_configuration"
public_ip_address_id = azurerm_public_ip.firstpip.id
subnet_id = azurerm_subnet.example.id
},
"1" = {
name = "second_pip_configuration"
public_ip_address_id = azurerm_public_ip.secondpip.id
},
"2" = {
name = "third_pip_configuration"
public_ip_address_id = azurerm_public_ip.thirdpip.id
}
}
EOT
default = {}
}

Issues with creating multiple vm's with Terraform for Azure

I am having issues with creating multiple virtual machines in Azure using Terraform. While creating the network interfaces I run into an error regarding the creation of the public ip address id:
I assume that I am using the count function incorrectly, or need a different approach entirely.
Code:
provider "azurerm" {
version = "~>2.0"
features {}
subscription_id = "XXXX"
client_id = "XXXX"
client_secret = "XXXX"
tenant_id = "XXXX"
}
resource "azurerm_resource_group" "rg" {
name = "${var.prefix}test_project"
location = var.location
tags = var.tags
}
resource "azurerm_virtual_network" "vnet" {
name = "${var.prefix}Vnet"
address_space = ["10.0.0.0/16"]
location = var.location
resource_group_name = azurerm_resource_group.rg.name
tags = var.tags
}
resource "azurerm_subnet" "subnet" {
name = "${var.prefix}Subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefix = "10.0.1.0/24"
}
resource "azurerm_public_ip" "publicip" {
name = "${var.prefix}PublicIP${count.index}"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
allocation_method = "Dynamic"
tags = var.tags
count = 2
}
resource "azurerm_network_security_group" "nsg" {
name = "${var.prefix}NetworkSecurityGroup"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
tags = var.tags
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_network_interface" "nic" {
name = "${var.prefix}NIC${count.index}"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
tags = var.tags
count = 2
ip_configuration {
name = "${var.prefix}NICConfig${count.index}"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = ["${element(azurerm_public_ip.publicip.id, count.index)}"]
}
}
resource "azurerm_network_interface_security_group_association" "example" {
count = length(azurerm_network_interface.nic)
network_interface_id = "${azurerm_network_interface.nic[count.index]}"
network_security_group_id = azurerm_network_security_group.nsg.id
}
resource "azurerm_linux_virtual_machine" "vm" {
count = 2
name = "${var.prefix}VM${count.index}"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = azurerm_network_interface.nic[count.index]
size = "Standard_DS1_v2"
tags = var.tags
os_disk {
name = "${var.prefix}OsDisk${count.index}"
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = lookup(var.sku, var.location)
version = "latest"
}
computer_name = "${var.computer_name}-${count.index}"
admin_username = var.admin_username
admin_password = var.admin_password
disable_password_authentication = false
}
Can anyone help me resolve this issue??
I'm pretty sure all you need to do is change
public_ip_address_id = ["${element(azurerm_public_ip.publicip.id, count.index)}"]
to
public_ip_address_id = ["${azurerm_public_ip.publicip[count.index].id}"]
In general, references like azurerm_public_ip.publicip.id work for singular resources (i.e. those that don't use count). So the use of element is kind of assuming a singular resource. As soon as count is used, resources start behaving like lists and need to be treated as such.

Map within a map in terraform variables

Does anyone know if it's possible with possibly code snipits representing whether I can create a map variable within a map variable in terraform variables?
variable "var" {
type = map
default = {
firstchoice = {
firstAChoice ="foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
}
}
If anyone has any insight to whether this is possible or any documentation that elaborates that would be great.
Yes, it's possible to have map variable as value of map variable key. Your variable just needed right indentation. Also I am putting ways to access that variable.
variable "var" {
default = {
firstchoice = {
firstAChoice = "foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
}
}
To access entire map value of a map key firstchoice, you can try following
value = "${var.var["firstchoice"]}"
output:
{
firstAChoice = foo
firstBChoice = bar
}
To access specific key of that map key (example firstAChoice), you can try
value = "${lookup(var.var["firstchoice"],"firstAChoice")}"
output: foo
Would this syntax be possible? ${var.var[firstchoice[firstAchoice]]}
With Terraform 0.12+ nested blocks are supported seamlessly. Extending #Avichal Badaya's answer to explain it using an example:
# Nested Variable
variable "test" {
default = {
firstchoice = {
firstAChoice = "foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
thirdchoice = {
thirdAChoice = {
thirdBChoice = {
thirdKey = "thirdValue"
}
}
}
}
}
# Outputs
output "firstchoice" {
value = var.test["firstchoice"]
}
output "FirstAChoice" {
value = var.test["firstchoice"]["firstAChoice"]
}
output "thirdKey" {
value = var.test["thirdchoice"]["thirdAChoice"]["thirdBChoice"]["thirdKey"]
}
Applying the above, you can verify that Terraform map nesting is now quite powerful and this makes a lot of things easier.
# Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
# Outputs:
firstchoice = {
"firstAChoice" = "foo"
"firstBChoice" = "bar"
}
thirdKey = thirdValue
For more complex structures and Rich Value Types, see HashiCorp Terraform 0.12 Preview: Rich Value Types

Google Script - Attachments to Drive

I have the code below, but i've been struggling to get it to work properly, either it creates duplicate folders every time i run it, or it doesn't upload the attachments and just creates the folders... I am also getting an error now that the newMail Uploads object does not have a .hasnext() function.
What i want to do, is have this script running and it puts attachments in a folder relating to their label -- So in the code below, all mail with the newMail label would go to a single folder, but i want to be able to extend the code further to run for multiple labels ect, so i want to check if the relevant folders exist and if not create them, if they exist they should be used.
-Edit, Now it is only taking the attachment from the first email from a certain address.
function startProcess()
{
var gmailLabels = "newLabel";
var driveFolder = "newFolder";
var archiveLabel = "Processed";
var moveToLabel = GmailApp.getUserLabelByName(archiveLabel);
if ( ! moveToLabel )
{
moveToLabel = GmailApp.createLabel(archiveLabel);
}
findFolder(gmailLabels, driveFolder, archiveLabel, moveToLabel);
}
function findFolder(gmailLabels, driveFolder, archiveLabel, moveToLabel)
{
var filter = "has:attachment label:" + gmailLabels;
var folder = DriveApp.getFoldersByName(driveFolder);
if (folder.hasNext()) {
folder = folder.next();
} else {
folder = DriveApp.createFolder(driveFolder);
}
callThreads(gmailLabels, driveFolder, archiveLabel, moveToLabel, filter, folder)
}
function callThreads(gmailLabels, driveFolder, archiveLabel, moveToLabel, filter, folder)
{
var threads = GmailApp.search(filter);
for (var x=0; x<threads.length; x++) {
var label = GmailApp.getUserLabelByName(gmailLabels);
var message = threads[x].getMessages()[x];
var desc = message.getSubject() + " #" + message.getId();
var att = message.getAttachments();
for (var z=0; z<att.length; z++) {
try {
file = folder.createFile(att[z]);
file.setDescription(desc);
}
catch (e) {
Logger.log(e.toString());
}
}
//threads[x].addLabel(moveToLabel);
label.removeFromThreads(threads);
//threads[x].moveToTrash();
}
}
This is just a suggestion. I haven't tested this code, it probably doesn't work. But that's not the point. I'm trying to show how you might create some order to your code so that it's easier to understand and debug:
function sendToGoogleDrive() {
makeNewFolders();
putEmailsIntoFolders();
};
function makeNewFolders() {
var gmailLabels = "newMail";
var driveFolder = "newMail";
var archiveLabel = "Processed";
var rootUploadFolders ="newMail Uploads";
var rootDriveFolder = DriveApp.getFolders();
var rootExist = false;
var childExist = false;
while (rootDriveFolder.hasNext()) {
var folder = rootDriveFolder.next();
if(folder.getName()==rootUploadFolders) {
var folderId = folder.getId();
if(folder.getName()!==driveFolder) {
var child = DriveApp.getFolderById(folderId).createFolder(driveFolder);
};
};
};
};
function putEmailsIntoFolders() {
};

i had upload file and some field items to document library i have made one field mandatory in that field but even then i am able to upload file

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SPContext.Current.Web.Url, SPUserToken.SystemAccount))
{
using (SPWeb web = site.OpenWeb())
{
web.AllowUnsafeUpdates = true;
SPFolder folder = web.Folders["ContractorDetails"];
SPFileCollection filecol = folder.Files;
Boolean replaceExistingFiles = true;
string filename = System.IO.Path.GetFileName(FileUpload.PostedFile.FileName);
byte[] contents = new byte[Convert.ToInt32(FileUpload.PostedFile.ContentLength)];
SPFile addedFile = filecol.Add(filename, contents, replaceExistingFiles);
SPItem newItem = addedFile.Item;
newItem["Title"] = ddlTitle.SelectedValue;
newItem["First Name"] = tbFirstName.Text;
newItem["Middle Name"] = tbMiddleName.Text;
newItem["Last Name"] = tbLastName.Text;
newItem["NT User Name"] = tbNtuser.Text;
newItem["Contract Firm"] = tbContractFirm.Text;
newItem["Employee Type"] = tbEmpType.Text;
newItem["Division"] = ddlDivision.SelectedValue;
newItem["Location"] = ddlLocation.SelectedValue;
newItem["Contract Start Date"] = dateTimeStart.SelectedDate;
newItem["Contract End Date"] = dateTimeEnd.SelectedDate;
newItem["Project Term"] = Convert.ToInt32(tbProjectTerm.Text);
// newItem["Manager"] = PeopleEditor1.t
newItem["Comments"] = tbcomments.Text;
newItem.Update();
addedFile.Update();
web.AllowUnsafeUpdates = false;
}
}
});
}
Can you just try to upload file from UI to check field is mandatory or not?