could you help me to understand
For example I have incremental changes to infrastructure, like [A] -> [B] -> [C], where [A] separately can be one server named i, [B] separately can be second server named j, and [C] separately can be third server named k. In total there should be 3 servers. Every state can be described as [A] = x, x + [B] = y, y + [C] = z where x, y, z are states in the middle.
My question are
How to organize incremental infrastructure changes for multiple modules in Terraform without affecting previous modules?
Is it possible to rollback changes in the middle of the chain eg. [B] and get x-state or we should follow chain from the last module [C] to required in the middle [B]?
At this time Terraform only considers two states[1]: the "current state" (the result of the previous Terraform run along with any "drift" in the mean time) and the "desired state" (described in configuration). Terraform's task is to identify the differences between these two states and determine which API actions are needed to move resources from their current state to the desired state.
This means that any multi-step transition cannot be orchestrated within Terraform alone. In your example, to add server j you would add it alone to the Terraform configuration, and run Terraform to create that server. You can then add server k to the configuration and run Terraform again. To automate this, an external process would need to generate these progressive configuration changes.
An alternative approach -- though not recommended for everyday use, since it can cause confusion in a collaborative environment where others can't easily see how this state was reached -- is to use the -target argument to specify one or more specific resources to operate on. In principle this allows adding both servers j and k to configuration but then using -target with only the resource representing j.
There is no formal support for rollback in Terraform. Terraform merely sees this as another state transition, "rolling forward". For example, if after creating server k you wish to revert to state [A], you would remove the configuration for server k (by reverting in version control, perhaps) and apply again. Terraform doesn't have any awareness of the fact that this is a "rollback", but it can see that the configuration no longer contains server k and thus know that it needs to be destroyed to reach the desired state.
One of your questions is about "affecting previous modules". In general, if no changes are made to a resource in your configuration (either the config changed, or the resource itself changed outside of Terraform's purview) then Terraform should not try to update it. If it did, that would be considered a bug. However, for larger systems it can be useful to split infrastructure across multiple top-level Terraform configurations that are each applied separately. If a remote backend is in use (recommended for collaborative environments) then the terraform_remote_state data source can be used to access the output values of one configuration from within another, thus allowing the creation of a tree or DAG of Terraform configurations. This adds complexity, so should be weighed carefully, but has the advantage of decreasing the "blast radius" of a particular change by taking unrelated resources out of consideration altogether.
[1] I am using "state" here in the general sense you used it, which is distinct from Terraform's physical idea of "state", a data file that contains a record of which resources existed at the last run.
Related
In Terraform is there some way to refer to the set of instances of a module?
I have several workloads that each need very similar (but still separate) infrastructure, and I also want to configure another item of infrastructure to be used in common between them.
For example, say each needs several pieces of infrastructure (AWS S3 bucket, SQS queue, and IAM role..) but with mostly equivalent attributes. I want to achieve this without code duplication (e.g., by writing a Terraform module to be reused for each instance, with input variables for name prefixes and specific IAM policies).
Is there a Terraform syntax for then making a reference to all of those instances in a single resource, and with minimal boilerplate? (Maybe something analogous to a classmethod, to define a common resource to only be created once, even if multiple instances of that module get created?) For example, to make a shared kubernetes config-map that contains an index of the generated addresses (bucket names or SQS URLs), as a mechanism for passing those addresses to the containerised workloads that will use them? Another example might be setting up a single load balancer or DNS server with rules referring individually to every service from this group.
Or does the problem have to be approached in the other direction, by starting with a list of parameter sets, and somehow looping over that list to create the infrastructure? (Requiring every instance of this kind to be specified together in the same file?)
The Terraform terminology for modularity is a child module that is called from multiple places in the configuration. The call uses the module block (where parameter values are passed). Since modules are directories, the child module will be defined somewhere outside of the configuration root module directory tree. The calling module can access output values exported from the child module.
You can use a single module call to create multiple instances, by putting a for_each argument in the module block, and passing a map (or set) through this meta-argument. The other argument expressions in the block can use the each object to refer to the particular for_each element corresponding to the current instance, as Terraform iterates through them. From outside of the module block (elsewhere in the calling module), the output values can themselves be accessed like a map.
You can use [for .. in .. : .. if ..] expressions to filter and transform maps. (Some transformations can also be performed more concisely by splat expressions.) Some resources (such as kubernetes_config_map) have arguments that take maps directly. (Also, some data or resources can accept a sequence of nested blocks, which can be generated from a map by using dynamic block syntax.)
Note, do not use the older feature count as an alternative to for_each. Otherwise, there is a documented tendency to produce unintended destruction and recreation of other infrastructure if one element of the list has to be decommissioned. Similarly, passing a list-derived set, instead of a map, to for_each can make indexing more cumbersome.
Thus for the OP's example, the approach would be to first create a parameterised child module defining the nearly-duplicated parts of the infrastructure (e.g., bucket, queue, role, etc). Make sure the child module has either inputs or outputs for any aspect that needs customisation (for example, output a handle for the created bucket resource, or at least for its auto-generated globally-unique name). Then have a module in your configuration that creates your whole collection of instances (by a single module block that sources the child module and using for_each). The customisation of individual instances (e.g., some having additional policies or other specific infrastructure) can be achieved by a combination of the parameter sets initially passed to the call, and supplementary resource blocks that each refer to outputs from an individual instance. Furthermore, the outputs can also be referred to collectively, for example parsing the module call outputs to form a list of bucket names (or addresses) and then passing this list to another resource (i.e., to a k8s config map). Again this must be done from the calling module; the child module does not have access to a list of instances of itself.
Context: even though this question is related to How to avoid "Objects have changed outside of Terraform"? it's not exactly the same.
I can't share my exact TF configuration but the idea is I'm getting an empty "Objects have changed outside of Terraform" warning message?
$ terraform plan
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply":
Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to
these changes.
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
No changes. Your infrastructure matches the configuration.
that doesn't display any potential changes.
When I copy my current state and then compare it against the new state after running terraform apply --auto-approve there're no changes either:
diff terraform.tfstate old.tfstate
4c4
< "serial": 25,
---
> "serial": 24,
217d216
< "data.foo.test",
219c218,219
< "data.bar.test2"
---
> "data.bar.test2",
> "data.bar.test2"
seems the only diff is ordering of resource in TF state. Is it TF bug or something?
$ terraform version
Terraform v0.15.4
Also found related issues on GitHub: https://github.com/hashicorp/terraform/issues/28776
This sort of odd behavior can occur in response to quirks in the way particular providers handle the "refresh" step. For backward compatibility with Terraform providers written using the older SDK designed for Terraform v0.11 and earlier, the plan renderer will suppress certain differences that tend to arise due to limitations/flaws in that SDK, such as a value being null before refresh but "" (empty string) after refresh.
Unfortunately if that sort of change is the only change then it can confuse Terraform in this way, where Terraform can see that there is a difference but then when it tries to render the difference it gets suppressed as legacy SDK noise and so there ends up being no difference to report.
This behavior was refined in later versions of Terraform, and so upgrading to the latest v1.x.y release may avoid the problem, assuming it's one of the quirks that the Terraform team already addressed.
I think the reason why you don't see any change to the state here could be that, since there were no changes to make in response to differences in your configuration, Terraform skipped the "apply" step and thus didn't actually commit the refreshed state. You can force Terraform to treat the refresh changes as something to be committed by creating and applying a refresh-only plan:
terraform apply -refresh-only
Assuming that the provider that's misbehaving here is consistent in how it misbehaves (that is: once it's returned a marginally different value, if it will keep returning that value), after applying the refresh only plan you would no longer see this message because future refreshes of the same object will not detect any such immaterial differences.
I'm having difficulty finding the documentation I need to work with Resources and resourcePools - there is no "resource API documentation" that I can find.
I would like to programatically create static resources (of a custom type) and then add these resources to a resourcePool. When creating the resources, I'd like to be able to specify their property values prior to adding them to the resourcePool. In my mind the code would look something like this:
Room myRoom;
myRoom = new Room("redRoom", 20);
addTo_myResourcePool(myRoom);
myRoom = new Room("greenRoom", 10);
addTo_myResourcePool(myRoom);
Does anyone know if there are ways to achieve this end?
This is a bit of a blind spot in AnyLogic. It can only be done indirectly:
Create an empty agent population with your agent type
Tell the resource pool to use that agent type
set the resource pool capacity as you need. The pool will create agents for your in that population (if the capacity is larger than the current number of resources)
If you want to manually create a resource, you must call myResourcePool.set_Capacity(myResourcePool.getCapacity()+1)
Destroying 1 resource works vice-versa.
Also, make sure to "destroy resources on capacity decrease" so the agents are destroyed from the population
There's a trick I use for this, that works sometimes, but I wouldn't generalize it as a final solution...I only do it when i have a few different unit characteristics, which seems to be your case.
step 1: create a population of resourcePools... each resource pool will correspond to one kind of agent characteristics, and all resourcePools use the same resource (agent) type
step 2: in the on new unit of the resource pool, you will use the index of the resourcePool population to generate the unit with particular characteristics... then you can just do something like resourcePool.get(i).set_capacity(whatever) in order to generate a resource unit with the exact characteristics you want
step 3: when you seize the resource, you will use alternatives... each resourcePool from the resourcePool population will be 1 option among the alternatives to use... you will need to create a function that retourns a ResourcePool[][]
step 4: you will use conditions to select the unit based on its characteristics (customize resource selection)
One option is to create a population of your resource agent, which I assume is of type room based on your code.
Then you have a function that will add a new agent to the population and return it to the caller.
And now you only need to add this to the new resource unit call in the resource pool object
Do not change the "Add Units to" option since we are already doing this in the function.
I tested this in a small model by having two buttons to increase and decrease the capacity during execution using
resourcePool.set_capacity(max(0, resourcePool.size() + 1));
and
remove_myResource(myResource.get(myResource.size()-1));
resourcePool.set_capacity(max(0, myResource.size()));
There are several related points here.
AnyLogic is designed such that you only change the agents in a resource pool via changing the capacity of the pool. This can be done directly via the pool's set_capacity function (if the pool's capacity is set to be defined "Directly"), but is more commonly done indirectly by linking the pool's capacity to a schedule (to represent shift patterns or any other pre-defined changes over time in capacity).
Resource pools will keep agents by default when the capacity decreases (i.e., they exist but are 'unavailable'), but you can set it to delete them. What you'll want will depend on what the resources and any resource-specific attributes/state represent.
If you need to address/access the agents in the resource pool independently from their use as seized/released resources you would have the pool store them in a custom population (rather than put them in the default population along with all other agents not created in a custom population) via the "Add units to" setting. Then you can access that population explicitly (e.g., loop through it) whenever you need to. Otherwise this aspect is not necessary.
If your resource pool is of custom agents with parameters (or other state that needs initialising in a particular way), the standard approach is to have direct/indirect capacity increases create the unit (with default parameter values) and then do any follow-on initialisation in the "On new unit" action (where you have access to the newly-created unit via the agent keyword).
You can alternatively do it via a dynamic expression for the "New resource unit" (as in Jaco's answer) but this doesn't particularly gain you anything (though it's equally valid). Yes, it's a bit more 'object-oriented', but there are loads of barriers AnyLogic puts up to doing things in a proper OO way anyway — as you say, the more 'obvious' OO approach would just be to have methods on the resource pool which add/remove agents. Basically, the standard 'design pattern' on blocks which create agents, like a Resource Pool or a Source block, is to let it create them with default info and then customise it in the appropriate action.
You could also use the "On startup" action of the resource pool agent as well / instead, but normally you would be using information from the process-containing agent to determine what state the new resource agent should have, which makes the "On startup" action less useful.
Finally(!), if you have an unchanging set of resource pool agents and it's easier to initialise them together (e.g., because there is some database input data for all of them that can thus just be looped through once), then just do that in your "On startup" action of the agent containing the resource pool (which runs after the resource pool has initialised and created default-state agents). That will require them in a custom population so you can loop through just those agents. But your question seemed to imply you were concerned about dynamic additions of resource agents.
But, as some of my other comments indicated, there are various subtleties/restrictions in how resource pools work (really based on them currently being designed as a pool where the explicit identity of each individual is not fully 'controllable') which mean that what you actually need may go beyond this.
A couple of examples:
If you wanted your resources to be explicit individual staff with info tracked across shifts (e.g., with their state changing to reflect experience or the history of tasks they've conducted), you have the complication that you can't control which resources (if you didn't delete them on capacity decrease) 'come back' when the capacity changes, so you may need more complex workarounds such as having separate resource pools for each distinct shift and seizing from resource sets including all such pools — only one of them (the active shift one) will have non-zero capacity at any given time unless the shifts overlap.
If capacity decreases (representing end-of-shift), you can't control / determine which resource agents are chosen to go off-shift. Plus there are issues regarding resource agents which are still active finishing a task before they actually 'leave' (given that shift-ends weren't set to pre-empt) — because they still exist until they finish, if you increase the capacity again whilst they're working AnyLogic won't create a new resource agent (it will just add this existing agent back as an 'active' agent in the pool). So that makes some other things harder...
I have two processes I want to juxtapose. The first is a Manual workflow that is well represented by the Process library. The second is a software System that performs the same work, but is better modelled as a state transition system (e.g. s/w component level).
Now in AnyLogic, state models are for agents, that can run through processes with animations (counts), or move across space. What if I want to use a state chart to run an agent through? so I have a System state chart/agent and a Job state chart/agent?
I want Jobs from Population A to go through the Manual process flow chart and Jobs from Population B to go through the System state flow chart, so I can juxtapose the processing costs. I then calculate various delays and resource allocations for each of the Jobs going through and compare them.
Can anyone explain how to setup a state chart as the base process, another agent will go through? Is this even possible?
Please help
Thanks
This will not work as you would like it to, for these reasons:
You can't send an Agent into a flowchart. (Not sure how AnyLogic is handling it internally, maybe a generic token, or no flow at all, just changes to the state).
In AnyLogic there can only be one state active (simple or combined state) per state chart, so you can't represent a population with several members.
Agents can't be in more then one flow at a time, so even if it would be possible to insert an Agent into a statechart, this limitation would also apply.
The conclusion of this is: State charts are suitable for modeling individual behaviour (inside one Agent), whereas process flows can be both used for individual behaviour (inside one Agent, running a dummy Agent through) as well as for groups (multiple Agents running through process).
The normal use case would be to add the state chart to the Agent type running through your process flow (as you already noted in your question), applying the changes caused by the state chart to the individual agent.
Can I modify state to convince terraform to only report actual changes and not scope changes?
Goal
Easily verify conversion of plan to module by reducing the delta.
Process
Create a module from a plan
Modify plan to use module passing all its required variables
Run terraform plan and expect NO changes
spoiler: all items in the plan get destroyed and recreated as the shift in scope.
- myplan.aws_autoscaling_group.foo
+ module.mymodule.aws_autoscaling_group.foo
Possible Solution
If I could update state as if I ran an apply without changing infrastructure, then I could run a plan and see just the difference between the actual infrastructure and my plan with not scope changes.
terraform state list
for each item
terraform state mv <oldval> module.mymodule.<oldval>
This works, but I'm moving from a plan that uses another module to a module that uses a module and mv on a module doesn't seem to work.
terraform state mv module.ecs_cluster module.mymodule.ecs_cluster
Error moving state: Unexpected value for InstanceType field: "ecs_cluster"
Please ensure your addresses and state paths are valid. No
state was persisted. Your existing states are untouched.