How do I use the "expand" param in the Azure SDK? - azure

Given this method signature:
func (client LoadBalancersClient) Get(ctx context.Context, resourceGroupName string, loadBalancerName string, expand string) (result LoadBalancer, err error)
How does one use the "expand" parameter? There appears to be zero documentation on how to format it and all I'm getting is InvalidExpandQueryOptionValue errors.
lbClient := network.NewLoadBalancersClient(subId)
lbClient.Authorizer = authr
lbResult, err := lbClient.Get(context.TODO(), rgName, lbName, "loadBalancingRules")
if err != nil {
panic(err)
}
Results in:
panic: network.LoadBalancersClient#Get: Failure responding to request:
StatusCode=400 -- Original Error: autorest/azure: Service returned an
error. Status=400 Code="InvalidExpandQueryOptionValue"
Message="$expand query option value is invalid. The resource does not
have property loadBalancingRules or the property does not represent a
reference to another resource." Details=[]
I've also tried $loadBalancingRules, {$loadBalancingRules}, and LoadBalancingRules.

I was hit by the same problem but when dealing with VNETs, Subnets and NSGs.
What happens is, when you query an object, by default, the properties of the referenced objects below in the hierarchy are not fetched. So if I were to get a list of NSGs (Network Security Groups), it would show me a list of subnets in the subnets property which would be references to subnets that are assigned to that NSG, however, the properties of those subnets like Name, ip address, etc will be set to None.
To overcome this, when querying for NSG I used
exapnd='subnets'
With this set, I can access the properties of the referenced subnets as well.

Related

Shall TF Provider delete resources from state if the resource is in "DELETING" state (similarly to 404)?

Context: I'm creating a new TF provider.
TF official docs say that
When you create something in Terraform but delete it manually, Terraform should gracefully handle it. If the API returns an error when the resource doesn't exist, the read function should check to see if the resource is available first. If the resource isn't available, the function should set the ID to an empty string so Terraform "destroys" the resource in state. The following code snippet is an example of how this can be implemented; you do not need to add this to your configuration for this tutorial.
if resourceDoesntExist {
d.SetID("")
return
}
It's pretty clear when resourceDoesntExist := response.code == 404 but what about the case where the resource is in DELETING state (which means that the resource is going to be removed in like 30 minutes and at that point GET request will start returning 404).
Shall it be treated as 404 too? What about the corresponding data source, shall it return an error?

Can I iterate through other resources in main.tf in readResource() when developing a TF provider?

Context: I'm developing a TF provider (here's the official guide from HashiCorp).
I run into the following situation:
# main.tf
resource "foo" "example" {
id = "foo-123"
name = "foo-name"
lastname = "foo-lastname"
}
resource "bar" "example" {
id = "bar-123"
parent_id = foo.example.id
parent_name = foo.example.name
parent_lastname = foo.example.lastname
}
where I have to declare parent_name and parent_lastname (effectively duplicate them) explicitly to be able to read these values that are necessary for read / create request for Bar resource.
Is it possible to use a fancy trick with d *schema.ResourceData in
func resourceBarRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
to avoid duplicated in my TF config, i.e. have just:
resource "foo" "example" {
id = "foo-123"
name = "foo-name"
lastname = "foo-lastname"
}
resource "bar" "example" {
id = "bar-123"
parent_id = foo.example.id
}
infer foo.example.name and foo.example.lastname just based on foo.example.id in resourceBarRead() somehow so I won't have to duplicate those fields in both resources?
Obviously, this is minimal example and let's assume I need both foo.example.name and foo.example.lastname to send a read / create request for Bar resource? In other words, can I iterate through other resource in TF state / main.tf file based on target ID to find its other attributes? It seems to be a useful feauture howerever it's not mentioned in HashiCorp's guide so I guess it's undesirable and I have to duplicate those fields.
In Terraform's provider/resource model, each resource block is independent of all others unless the user explicitly connects them using references like you showed.
However, in most providers it's sufficient to pass only the id (or similar unique identifier) attribute downstream to create a relationship like this, because the other information about that object is already known to the remote system.
Without knowledge about the particular remote system you are interacting with, I would expect that you'd be able to use the value given as parent_id either directly in an API call (and thus have the remote system connect it with the existing object), or to make an additional read request to the remote API to look up the object using that ID and obtain the name and lastname values that were saved earlier.
If those values only exist in the provider's context and not in the remote API then I don't think there will be any alternative but to have the user pass them in again, since that is the only way that local values (as opposed to values persisted in the remote API) can travel between resources.

Terraform Data Source Meaning

I am new to Terraform and trying to understand data sources. I have read the documentation and this StackOverflow post, but I'm still unclear about the use cases of data source.
I have the following block of code:
resource "azurerm_resource_group" "rg" {
name = "example-resource-group"
location = "West US 2"
}
data "azurerm_resource_group" "test" {
name = "example-resource-group"
}
But I get a 404 error:
data.azurerm_resource_group.test: data.azurerm_resource_group.test: resources.GroupsClient#Get: Failure responding to request:
StatusCode=404 -- Original Error: autorest/azure: Service returned an
error. Status=404 Code="ResourceGroupNotFound" Message="Resource group
'example-resource-group' could not be found."
I don't understand why the resource group is not found. Also, I am unclear about the difference between data and variable and when should I use which.
Thanks
I have provided a detailed explanation of what a data source is in this SO answer. To summarize:
Data sources provide dynamic information about entities that are not managed by the current Terraform configuration
Variables provide static information
Your block of code doesn't work because the resource your data source is referencing hasn't been created yet. During the planning phase, Terraform will try to find a resource group named example-resource-group, but it won't find it, and so it aborts the whole run. The ordering of the blocks makes no difference to the order they are applied.
If you remove the data block, run terraform apply, and then add the data block back in, it should work. However, data sources are used to retrieve data about entities that are not managed by your Terraform configuration. In your case, you don't need the data.azurerm_resource_group.test data source, you can simply use the exported attributes from the resource. In the case of azurerm_resource_group, this is a single id attribute.
Think of a data source as a value you want to read from somewhere else.
A variable is something you define when you run the code.
When you use the data source for azurerm_resource_group terraform will search for an existing resource that has the name you defined in your data source block.
Example
data "azurerm_resource_group" "test" {
name = "example-resource-group"
}
Quoting #ydaetskcoR from the comment below about 404 error:
It's 404ing because the data source is running before the resource
creates the thing you are looking for. You would use a data source
when the resource has already been created previously, not in the same
run as the resource you are creating.

Terraform: Undefined remote state handling

I have a remote state attribute called subnets which is stored in: data.terraform_remote_state.alb.subnets
Depending on what I'm deploying, this attribute either exists or doesn't exist.
When I try to create an ECS cluster, it requires an input of the subnet groups in which I would like to either use:
data.terraform_remote_state.alb.subnets
or
var.vpc_subnets (the subnets of the VPC)
Unfortunately, because of the way the interpolation works, it needed to be hacked together:
"${split(",", length(var.vpc_subnets) == 0 ? join(",",data.terraform_remote_state.alb.subnets) : join(",",var.vpc_subnets))}"
(Refering to: https://github.com/hashicorp/terraform/issues/12453)
However, because Terraform does not seem to 'lazily' evaluate ternary operators, it throws me the following error even if var.vpc_subnets is NOT zero:
Resource 'data.terraform_remote_state.alb' does not have attribute 'subnets' for variable 'data.terraform_remote_state.alb.subnets'
How can I properly handle remote state resources that could be undefined?
EDIT: Typo: Subnet->Subnets
Managed to figure it out.
When using Terraform Remote State, you have the ability to set a default: https://www.terraform.io/docs/providers/terraform/d/remote_state.html
This works in my situation when my data "terraform_remote_state.alb.subnets does not return a value. I can preset the variable to be "" and use locals to do a check for this variable.
Will it be subnet or subnets?
Suppose you have below data source:
data "terraform_remote_state" "alb" {
backend = "s3"
config {
name = "alb"
}
}
You need check the remote state attribute have any outputs with name subnet or not. Or the key name is subnets, you need confirm by yourself.

Dns Server (edns)opt type of resource record

i want to request for an opt resource record message to dns server bind 9.
but i don't know the format and the server configration.
http://www.ietf.org/rfc/rfc2671.txt this doc is the edns doc.
i create the message formatted following the doc,but it doesn't work. server tell me format error.
the req message:
Question Record:
QName:a6.debian.com
QType:0x41(OPT type)
QClass:0x01(Internet)
Additional Record:
Resource Name:0xc0,0x0c( pointer to QName)
Resource Type:0x41
ResourceClass:512(udp payload size)
TimeToLive:0x1EF0000(split to extent-code version and Z)
ResourceDataLength:0x08
Rdata:(OPTRdata):
OptCode:0x4000
OptLength:0x04
OptData:0x0A,0x0A,0x0A,0x0A
What's wrong???Could you help me?
There's no OPT type record. OPT is pseudo-record type. You can not query for it. You use OPT to pass some specific parameters to DNS server, like EDNS. Besides it can appear in "ADDITIONAL" section only

Resources