Appendix B. Solutions to exercises

Exercise 1.1

Choose an infrastructure script or configuration in your organization. Assess whether it adheres to the principles of IaC. Does it promote reproducibility, use idempotency, help with composability, and ease evolvability?

Answer:

You can use the following steps to identify whether your script of configuration follows IaC principles:

  • Reproducibility—Copy and paste the script or configuration, share it with someone else, and ask them to create the resources without modifying or changing the configuration.

  • Idempotency—Run the script a few times. It should not change your infrastructure.

  • Composability—Copy a portion of the configuration and build it on top of other infrastructure resources.

  • Evolvability—Add a new infrastructure resource to the configuration and verify that you can make changes without affecting other resources.

Exercise 2.1

Does the following use the imperative or declarative style of configuring infrastructure?

if __name__ == "__main__":
   update_packages()
   read_ssh_keys()
   update_users()
   if enable_secure_configuration:
       update_ip_tables()

Answer:

The code snippet uses the imperative style of configuring infrastructure. It defines how to configure a server step-by-step in a specific sequence rather than declaring a specific target configuration.

Exercise 2.2

Which of the following changes benefit from the principle of immutability? (Choose all that apply.)

A) Reducing a network to have fewer IP addresses

B) Adding a column to a relational database

C) Adding a new IP address to an existing DNS entry

D) Updating a server’s packages to backward-incompatible versions

E) Migrating infrastructure resources to another region

Answer:

The correct answers are A, D, and E. Each change benefits from a new set of resources that implement the change. If you try to make the changes in place, you may accidentally bring down the existing system. For example, reducing a network to have fewer IP addresses may displace any resources using the network.

Updating packages to backward-incompatible versions means that a broken package update can affect the server’s ability to handle user requests. Migrating infrastructure resources to another region takes time. Not all cloud provider regions support every type of resource. Creating new resources helps mitigate problems with migration. You can make changes for answers B and C mutably, likely without affecting the system.

Exercise 3.1

To which module pattern(s) does the following IaC apply? (Choose all that apply.)

if __name__ == "__main__":
  environment = 'development'
  name = f'{environment}-hello-world'
  cidr_block = '10.0.0.0/16'
 
  # NetworkModule returns a subnet and network
  network = NetworkModule(name, cidr_block)
 
  # Tags returns a default list of tags
  tags = TagsModule()
 
  # ServerModule returns a single server
  server = ServerModule(name, network, tags)

A) Factory

B) Singleton

C) Prototype

D) Builder

E) Composite

Answer:

The infrastructure as code applies A, C, and E. The network module uses the composite pattern to compose a subnet and a network. The tag module uses the prototype pattern to return status metadata. The server module uses the factory pattern to return a single server based on name, network, and tags.

The code does not use singleton or builder patterns. It does not create singular global resources or internal logic to build specific resources.

Exercise 4.1

How can we better decouple the database’s dependency on the network via the following IaC?

class Database:
  def __init__(self, name):
    spec = {
      'name': name,
      'settings': {
        'ip_configuration': {
          'private_network': 'default'
        }
      }
    }

A) The approach adequately decouples the database from the network.

B) Pass the network ID as a variable instead of hardcoding it as default.

C) Implement and pass a NetworkOutput object to the database module for all network attributes.

D) Add a function to the network module to push its network ID to the database module.

E) Add a function to the database module to call the infrastructure API for the default network ID.

Answer:

You can implement the adapter pattern to output the network attributes (C). The database chooses which network attributes it uses, such as network ID or CIDR block. This approach best follows the principle of dependency injection. While D does implement dependency inversion, it does not implement inversion of control. Answer E does implement dependency injection but continues to hardcode the network ID.

Exercise 6.1

You notice that a new version of a load balancer module is breaking your DNS configuration. A teammate updated the module to output private IP addresses instead of public IP addresses. What can you do to help your team better remember the module needs public IP addresses?

A) Create a separate load balancer module for private IP addresses.

B) Add module contract tests to verify that the module outputs both private and public IP addresses.

C) Update the module’s documentation with a note that it needs public IP addresses.

D) Run integration tests on the module and check the IP address is publicly accessible.

Answer:

Rather than creating a new module, you can add a contract test to help the team remember that high-level resources need both private and public IP addresses (B). You could create a separate load balancer module (A). However, it may not help your team remember that specific modules must output specific variables. Updating the module’s documentation (C) means that your team must remember to read the documentation first. Integration tests have a time and financial cost for running (D) when contract tests adequately solve the problem.

Exercise 6.2

You add some firewall rules to allow an application to access a new queue. Which combination of tests would be most valuable to your team for the change?

A) Unit and integration tests

B) Contract and end-to-end tests

C) Contract and integration tests

D) Unit and end-to-end tests

Answer:

The two most valuable tests would be unit and end-to-end tests (D). Unit tests will help ensure that someone doesn’t remove the new rules going forward. End-to-end tests check that the application can access the queue successfully. Contract tests would not provide any help because you do not need to test inputs and outputs for firewall rules.

Exercise 7.1

Choose a standard infrastructure change in your organization. What do you need in order to confidently continuously deliver the change to production? What about continuous deployment? Outline or diagram stages in your delivery pipelines for both.

Answer:

As you go through this exercise, consider the following:

  • Do you have unit, integration, or end-to-end tests?

  • What kind of branching model do you use?

  • Does your company have compliance requirements? For example, two people must approve the change before production.

  • What happens when someone needs to make a change?

Exercise 9.1

Consider the following code:

if __name__ == "__main__":
  network.build()
  queue.build(network)
  server.build(network, queue)
  load_balancer.build(server)
  dns.build(load_balancer)

The queue depends on the network. The server depends on the network and queue. How would you run a blue-green deployment to upgrade the queue with SSL?

Answer:

You create a green queue with SSL enabled. Then, you create a new green server that depends on the queue. Test that the application on the green server can access the queue with the SSL configuration. If it passes, you can add the green server to the load balancer. Gradually send traffic through the green server by using a canary deployment until you confirm that all requests succeed. Then, you remove the original server and queue.

Optionally, you can omit the step creating the green server and send traffic from the original server to SSL. However, this approach may not allow you to run a canary deployment. You update the server’s configuration to directly communicate with the new queue.

Exercise 10.1

Given the following code, what order and grouping of resources would you use to refactor and break down the monolith?

if __name__ == "__main__":
  zones = ['us-west1-a', 'us-west1-b', 'us-west1-c']
  project.build()
  network.build(project)
  for zone in zones:
    subnet.build(project, network, zone)
  database.build(project, network)
  for zone in zones:
    server.build(project, network, zone)
  load_balancer.build(project, network)
  dns.build()

A) DNS, load balancer, servers, database, network + subnets, project

B) Load balancer + DNS, database, servers, network + subnets, project

C) Project, network + subnets, servers, database, load balancer + DNS

D) Database, load balancer + DNS, servers, network + subnets, project

Answer:

The order and grouping that mitigates the risk of refactor starts with DNS and proceeds with the load balancer, queue, database, servers, network and subnets, and project (A). You want to start with DNS as the highest-level dependency and evolve it separately from the load balancer, since the load balancer requires project and network attributes. Refactor the next resources as follows: the servers, database, network, and project.

You refactor the servers before the database because servers do not manage data directly but depend on the database. If you do not feel confident that you can refactor the database, at least you’ve moved the servers out of the monolith! You can always leave the database in the monolith with the network and project. Refactoring the bulk of resources out of the monolith sufficiently decouples the system to scale.

Exercise 11.1

A team reports that that its application can no longer connect to another application. The application worked last week, but requests have failed since Monday. The team has made no changes to its application and suspects the problem may be a firewall rule. Which steps can you take to troubleshoot the problem? (Choose all that apply.)

A) Log into the cloud provider and check the firewall rules for the application.

B) Deploy new infrastructure and applications to a green environment for testing.

C) Examine the changes in IaC for the application.

D) Compare the firewall rules in the cloud provider with IaC.

E) Edit the firewall rules and allow all traffic between the applications.

Answer:

The steps are A, C, and D. You can troubleshoot by checking for any drift in firewall rules. If you do not find drift, you can search the IaC running the application for other discrepancies. From a troubleshooting perspective, this problem may not need a new environment for testing or allowing all traffic between applications.

Exercise 12.1

Given the following code, which of the following statements are true? (Choose all that apply.)

HOURS_IN_MONTH = 730
MONTHLY_BUDGET = 5000
DATABASE_COST_PER_HOUR = 5
NUM_DATABASES = 2
BUFFER = 0.1
 
 
def test_monthly_budget_not_exceeded():
   total = HOURS_IN_MONTH * NUM_DATABASES * DATABASE_COST_PER_HOUR
   assert total < MONTHLY_BUDGET + MONTHLY_BUDGET * BUFFER

A) The test will pass because the cost of the database is within the budget.

B) The test estimates the monthly cost of databases.

C) The test does not account for different database types.

D) The test calculates the monthly cost per database instance.

E) The test includes a buffer of 10% for any cost overage as a soft mandatory policy.

Answer:

The answers are B, C, and E. The total estimated cost of the databases per month is $7,300, while the monthly budget with a 10% buffer is $5,500, which means the test fails. The test itself does not account for different database types or calculate monthly cost per database instance. It calculates based on the hourly rate and the number of databases. Adding a 10% buffer to the monthly budget creates a soft mandatory policy for the budget. Small changes that result in minor cost overage will reduce the friction of delivery to production but flag any major cost changes.

Exercise 12.2

Imagine you have three servers. You examine their utilization and notice the following:

  • You need one server to serve the minimum traffic.

  • You need three servers to serve the maximum traffic.

  • The server handles traffic 24 hours a day, seven days a week.

What can you do to optimize the cost of the servers for the next month?

A) Schedule the resources to stop on the weekends.

B) Add an autoscaling_policy to scale based on memory.

C) Set an expiration of three hours for all servers.

D) Change the servers to smaller CPU and memory machine types.

E) Migrate the application to containers and pack applications more densely on servers.

Answer:

You can add an autoscaling_policy to scale based on memory (B). You cannot schedule a resource stop on weekends because you need at least one server on weekends. Setting an expiration or downsizing the servers does not help with cost in this situation. Migrating applications to containers involves a long-term solution that will not optimize cost for the next month.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset