One place for hosting & domains

      How To Set Up Visual Studio Code (VS Code) for PHP Development


      Introduction

      Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems.

      With a large collection of extensions for many programming languages, VS Code can be customized to serve a wide variety of development needs. In this guide, you’ll learn how to set up Visual Studio Code for working on PHP projects.

      The gif shows the VS Code interface for PHP development

      Prerequisites

      To follow this guide, you’ll need to download and install the appropriate version of Visual Studio Code for your operating system.

      The instructions in this guide were validated on an Ubuntu 20.04 desktop computer, but they should work seamlessly in all operating systems that are supported by VS Code. Please note, though, that keyboard shortcut keys may need to be slightly modified for compatibility with MacOS systems.

      Right after installation, VS Code already recognizes PHP code and helps with syntax highlighting, basic debugging, and code indentation. These features are suitable for quick edits or when working with individual PHP scripts. Working in larger projects, though, can become difficult without more context around the code and how each file interacts and integrates within the larger project.

      There are a number of VS Code extensions that can help speed up your productivity when working on PHP projects. In this guide, we’ll install and set up PHP Intelephense, a popular PHP extension for VS Code that provides several advanced features such as improved code completion, better navigation between components, rich information tooltips on mouse hover, code auto formatting, and real time error reporting based on static code analysis.

      Open the extensions tab by clicking on the last icon on the left menu bar, or by pressing CTRL + SHIFT + X. This will bring up a sidebar menu with a search box and a list of popular or recommended extensions. Type “php” or “intelephense” to locate the PHP Intelephense extension. Click on the Install button to install and enable the extension.

      The gif shows how to access the extensions menu on the left sidebar

      Once installation is complete, Intelephense’s official documentation recommends that you disable the built-in PHP Language Features extension that comes with VS Code.

      To disable this extension, type @builtin php in the Extensions search box to locate the built-in PHP extensions. Then click on the settings icon for the PHP Language Features extension, and click the Disable option in the drop-down menu.

      The gif shows how to disable the builtin PHP Language Features extension

      If you have any files open in VS Code, you’ll need to reload the editor to apply the changes.

      You can install other extensions by following the same process described for the PHP Intelephense extension, but be aware that some extensions will require additional software to be installed on your system. Check the extension documentation to make sure you have requirements set appropriately.

      Step 2 — Importing or Creating a New PHP Project

      To import an existing PHP project into VS Code, click on the first icon on the left menu bar or type CTRL + SHIFT + E to access the file explorer. Click the Open Folder button and select your project’s directory. In case you are creating a new project, you can create a new folder and select that as your project directory.

      The gif shows how to import a PHP project to VS Code

      The explorer window will now show the project’s directory tree, giving you quick access to files and directories on your project.

      Step 3 — Customizing Theme and Editor Font (Optional)

      Although not necessary, it is often a good idea to customize the appearance of your editor to make it more comfortable to use in the longer term. As a developer you may spend several hours every day looking at an IDE such as VS Code, and for that reason it’s important to make sure the editor font has an appropriate size, and the contrast is enough for good readability without tiring your eyes too quickly.

      You may want to experiment with different themes and fonts in order to find a custom setup that works well for you.

      Changing VS Code Theme

      VS Code comes with a few different themes that allow you to change the colors used in the editor interface and in the code highlighting. Both dark and light styles are included by default.

      Go to File -> Preferences -> Color Theme or type CTRL + K + T to select a different theme for VS Code.

      The gif shows how to change VS Code theme

      You can also install theme extensions to further customize VS Code’s appearance. If you search for theme within the extensions tab (CTRL + SHIFT + X), you’ll find several theme extensions in different styles and colors, including color schemes ported from other popular editors and platforms.

      Adjusting the Font

      Although the default VS Code font settings will be good enough for many users, you may want to adjust the size and the type of the editor font for increased readability.

      If you would like to modify the font size or change to another type, you can go to File -> Preferences -> Settings, then choose Text Editor on the left menu. Then, click on Font in the submenu that opens up. That section contains font family and size settings which you can tweak as preferred.

      The gif shows how to change VS Code editor font size

      The changes are immediately saved and applied to all current open files.

      Conclusion

      Visual Studio Code is a lightweight yet powerful code editor that can be customized to suit the needs of most developers. In this guide, you learned how to install and configure the PHP Intelephense extension for extra support while working on PHP projects, how to import and create new projects within VS Code, and how to customize the editor appearance for a more comfortable coding experience.

      For more tutorials on Visual Studio Code, check our VS Code tag page. If you’d like to learn more about PHP development, you can refer to our How To Code in PHP series for an introduction to the language, and our PHP tag page for more PHP tutorials.



      Source link

      How To Create Reusable Blocks of Code with Vue Single-File Components


      The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.

      Introduction

      When creating a web application using Vue.js, it’s a best practice to construct your application in small, modular blocks of code. Not only does this keep the parts of your application focused, but it also makes the application easier to update as it grows in complexity. Since an app generated from the Vue CLI requires a build step, you have access to a Single-File Components (SFC) to introduce modularity into your app. SFCs have the .vue extension and contain an HTML <template>, <script>, and <style> tags and can be implemented in other components.

      SFCs give the developer a way to create their own HTML tags for each of their components and then use them in their application. In the same way that the <p> HTML tag will render a paragraph in the browser, and hold non-rendered functionality as well, the component tags will render the SFC wherever it is placed in the Vue template.

      In this tutorial, you are going to create a SFC and use props to pass data down and slots to inject content between tags. By the end of this tutorial, you will have a general understanding of what SFCs are and how to approach code re-usability.

      Prerequisites

      Step 1 — Setting Up the Project

      In this tutorial, you are going to be creating an airport card component that displays a number of airports and their codes in a series of cards. After following the Prerequisites section, you will have a new Vue project named sfc-project. In this section, you will import data into this generated application. This data will be an array of objects consisting of a few properties that you will use to display information in the browser.

      Once the project is generated, open your terminal and cd or change directory into the root src folder:

      From there, create a new directory named data with the mkdir command, then create a new file with the name us-airports.js using the touch command:

      • mkdir data
      • touch data/us-airports.js

      In your text editor of choice, open this new JavaScript file and add in the following local data:

      sfc-project/data/us-airports.js

      export default [
        {
          name: 'Cincinnati/Northern Kentucky International Airport',
          abbreviation: 'CVG',
          city: 'Hebron',
          state: 'KY'
        },
        {
          name: 'Seattle-Tacoma International Airport',
          abbreviation: 'SEA',
          city: 'Seattle',
          state: 'WA'
        },
        {
          name: 'Minneapolis-Saint Paul International Airport',
          abbreviation: 'MSP',
          city: 'Bloomington',
          state: 'MN'
        }
      ]
      

      This data is an array of objects consisting of a few airports in the United States. This will be rendered in a Single-File Component later in the tutorial.

      Save and exit the file.

      Next, you will create another set of airport data. This data will consist of European airports. Using the touch command, create a new JavaScript file named eu-airports.js:

      • touch data/eu-airports.js

      Then open the file and add the following data:

      sfc-project/data/eu-airports.js

      export default [
        {
          name: 'Paris-Charles de Gaulle Airport',
          abbreviation: 'CDG',
          city: 'Paris',
          state: 'Ile de France'
        },
        {
          name: 'Flughafen München',
          abbreviation: 'MUC',
          city: 'Munich',
          state: 'Bavaria'
        },
        {
          name: 'Fiumicino "Leonardo da Vinci" International Airport',
          abbreviation: 'FCO',
          city: 'Rome',
          state: 'Lazio'
        }
      ]
      

      This set of data is for European airports in France, Germany, and Italy, respectively.

      Save and exit the file.

      Next, in the root directory, run the following command in your terminal to start your Vue CLI application running on a local development server:

      This will open the application in your browser on localhost:8080. The port number may be different on your machine.

      Visit the address in your browser. You will find the following start up screen:

      Vue default template page

      Next, start a new terminal and open your App.vue file in your src folder. In this file, delete the img and HelloWorld tags in the <template> and the components section and import statement in the <script>. Your App.vue will resemble the following:

      sfc-project/src/App.vue

      <template>
      
      </template>
      
      <script>
      export default {
        name: 'App',
      }
      </script>
      
      <style>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      </style>
      

      After this, import the us-airports.js file that you created earlier. In order to make this data reactive so you can use it in the <template>, you need to import the ref function from vue. You will need to return the airport reference so the data can be used in the HTML template.

      Add the following highlighted lines:

      sfc-project/src/App.vue

      <template>
        <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation" class="card">
            <p>{{ airport.abbreviation }}</p>
            <p>{{ airport.name }}</p>
            <p>{{ airport.city }}, {{ airport.state }}</p>
          </div>
        </div>
      </template>
      
      <script>
      import { ref } from 'vue'
      import data from '@/data/us-airports.js'
      
      export default {
        name: 'App',
        setup() {
          const airports = ref(data)
      
          return { airports }
        }
      }
      </script>
      ...
      

      In this snippet, you imported the data and rendered it using <div> elements and the v-for directive in the template.

      At this point, the data is imported and ready to be used in the App.vue component. But first, add some styling to make the data easier for users to read. In this same file, add the following CSS in the <style> tag:

      sfc-project/src/App.vue

      ...
      <style>
      #app { ... }
      
      .wrapper {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-column-gap: 1rem;
        max-width: 960px;
        margin: 0 auto;
      }
      
      .card {
        border: 3px solid;
        border-radius: .5rem;
        padding: 1rem;
        margin-bottom: 1rem;
      }
      
      .card p:first-child {
        font-weight: bold;
        font-size: 2.5rem;
        margin: 1rem 0;
      }
      
      .card p:last-child {
        font-style: italic;
        font-size: .8rem;
      }
      </style>
      

      In this case, you are using CSS Grid to compose these cards of airport codes into a grid of three. Notice how this grid is set up in the .wrapper class. The .card class is the card or section that contains each airport code, name, and location. If you would like to learn more about CSS, check out our How To Style HTML with CSS.

      Open your browser and navigate to localhost:8080. You will find a number of cards with airport codes and information:

      Three airport cards rendering the data for the US airports dataset

      Now that you have set up your initial app, you can refactor the data into a Single-File Component in the next step.

      Step 2 — Creating a Single-File Component

      Since Vue CLI uses Webpack to build your app into something the browser can read, your app can use SFCs or .vue files instead of plain JavaScript. These files are a way for you to create small blocks of scalable and reusable code. If you were to change one component, it would be updated everywhere.

      These .vue components usually consist of these three things: <template>, <script>, and <style> elements. SFC components can either have scoped or unscoped styles. When a component has scoped styles, that means the CSS between the <style> tags will only affect the HTML in the <template> in the same file. If a component has un-scoped styles, the CSS will affect the parent component as well as its children.

      With your project successfully set up, you are now going to break these airport cards into a component called AirportCards.vue. As it stands now, the HTML in the App.vue is not very reusable. You will break this off into its own component so you can import it anywhere else into this app while preserving the functionality and visuals.

      In your terminal, create this .vue file in the components directory:

      • touch src/components/AirportCards.vue

      Open the AiportCards.vue component in your text editor. To illustrate how you can re-use blocks of code using components, move most of the code from the App.vue file to the AirportCards.vue component:

      sfc-project/src/components/AirportCards.vue

      <template>
        <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation" class="card">
            <p>{{ airport.abbreviation }}</p>
            <p>{{ airport.name }}</p>
            <p>{{ airport.city }}, {{ airport.state }}</p>
          </div>
        </div>
      </template>
      
      <script>
      import { ref } from 'vue'
      import data from '@/data/us-airports.js'
      
      export default {
        name: 'Airports',
        setup() {
          const airports = ref(data)
      
          return { airports }
        }
      }
      </script>
      
      <style scoped>
      .wrapper {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-column-gap: 1rem;
        max-width: 960px;
        margin: 0 auto;
      }
      
      .card {
        border: 3px solid;
        border-radius: .5rem;
        padding: 1rem;
        margin-bottom: 1rem;
      }
      
      .card p:first-child {
        font-weight: bold;
        font-size: 2.5rem;
        margin: 1rem 0;
      }
      
      .card p:last-child {
        font-style: italic;
        font-size: .8rem;
      }
      </style>
      

      Save and close the file.

      Next, open your App.vue file. Now you can clean up the App.vue component and import AirportCards.vue into it:

      sfc-project/src/App.vue

      <template>
        <AirportCards />
      </template>
      
      <script>
      import AirportCards from '@/components/Airports.vue'
      
      export default {
        name: 'App',
        components: {
          AirportCards
        }
      }
      </script>
      
      <style scoped>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      </style>
      

      Now that AirportCards is a standalone component, you have put it in the <template> HTML as you would a <p> tag.

      When you open up localhost:8080 in your browser, nothing will change. The same three airport cards will still display, because you are rendering the new SFC in the <AirportCards /> element.

      Next, add this same component in the template again to illustrate the re-usability of components:

      /src/App.vue

      <template>
        <AirportCards />
        <airport-cards />
      </template>
      ...
      

      You may notice that this new instance of AirportCards.vue is using kebab-case over PascalCase. When referencing components, Vue does not care which one you use. All capitalized words and letters will be separated by a hyphen and will be lower case. The same applies to props as well, which will be explained in the next section.

      Note: The case that you use is up to personal preference, but consistency is important. Vue.js recommends using kebab-case as it follows the HTML standard.

      Open the browser and visit localhost:8080. You will find the cards duplicated:

      The US airport cards rendered twice in the browser.

      This adds modularity to your app, but the data is still static. The row of cards is useful if you want to show the same three airports, but changing the data source would require changing the hard-coded data. In the next step, you are going to expand this component further by registering props and passing data from the parent down to the child component.

      Step 3 — Leveraging Props to Pass Down Data

      In the previous step, you created an AirportCards.vue component that rendered a number of cards from the data in the us-airports.js file. In addition to that, you also doubled the same component reference to illustrate how you can easily duplicate code by adding another instance of that component in the <template>.

      However, leaving the data static will make it difficult to change the data in the future. When working with SFCs, it can help if you think of components as functions. These functions are components that can take in arguments (props) and return something (HTML). For this case, you will pass data into the airports parameter to return dynamic HTML.

      Open the AirportCards.vue component in your text editor. You are currently importing data from the us-airports.js file. Remove this import statement, as well as the setup function in the <script> tag:

      sfc-project/src/components/AirportCards.vue

      ...
      <script>
      
      export default {
        name: 'Airports',
      }
      </script>
      ...
      

      Save the file. At this point, nothing will render in the browser.

      Next, move forward by defining a prop. This prop can be named anything; it just describes the data coming in and associates it with a name.

      To create a prop, add the props property in the component. The value of this is a series of key/value pairs. The key is the name of the prop, and the value is the description of the data. It’s best to provide as much description as you can:

      sfc-project/src/components/AirportCards.vue

      ...
      <script>
      
      export default {
        name: 'Airports',
        props: {
          airports: {
            type: Array,
            required: true
          }
        }
      }
      </script>
      ...
      

      Now, in AirportCard.vue, the property airports refers to the data that is passed in. Save and exit from the file.

      Next, open the App.vue component in your text editor. Like before, you will need to import data from the us-airports.js file and import the ref function from Vue to make it reactive for the HTML template:

      sfc-project/src/App.vue

      <template>
        <AirportCards :airports="usAirports" />
        <airport-cards />
      </template>
      
      <script>
      import { ref } from 'vue'
      import AirportCards from '@/components/Airports.vue'
      import usAirportData from '@/data/us-airports.js'
      
      export default {
        name: 'App',
        components: {
          AirportCards
        },
        setup() {
          const usAirports = ref(usAirportData)
      
          return { usAirports }
        }
      }
      </script>
      

      If you open your browser and visit localhost:8080, you will find the same US airports as before:

      US airports rendered on cards in the browser

      There is another AirportCards.vue instance in your template. Since you defined props within that component, you can pass any data with the same structure to render a number of cards from different airports. This is where the eu-airports.js file from the initial setup comes in.

      In App.vue, import the eu-airports.js file, wrap it in the ref function to make it reactive, and return it:

      /src/App.vue

      <template>
        <AirportCards :airports="usAirports" />
        <airport-cards :airports="euAirports" />
      </template>
      
      <script>
      ...
      import usAirportData from '@/data/us-airports.js'
      import euAirportData from '@/data/eu-airports.js'
      
      export default {
        ...
        setup() {
          const usAirports = ref(usAirportData)
          const euAirports = ref(euAirportData)
      
          return { usAirports, euAirports }
        }
      }
      </script>
      ...
      

      Open your browser and visit localhost:8080. You will find the European airport data rendered below the US airport data:

      US airport data rendered as cards, followed by European airport data rendered in the same way.

      You have now successfully passed in a different dataset into the same component. With props, you are essentially re-assigning data to a new name and using that new name to reference data in the child component.

      At this point, this application is starting to become more dynamic. But there is still something else that you can do to make this even more focused and re-usable. In Vue.js, you can use something called slots. In the next step, you are going to create a Card.vue component with a default slot that injects the HTML into a placeholder.

      Step 4 — Creating a General Card Component Using Slots

      Slots are a great way to create re-usable components, especially if you do not know if the HTML in that component will be similar. In the previous step, you created another AirportCards.vue instance with different data. In that example, the HTML is the same for each. It’s a <div> in a v-for loop with paragraph tags.

      Open your terminal and create a new file using the touch command. This file will be named Card.vue:

      • touch src/components/Card.vue

      In your text editor, open the new Card.vue component. You are going to take some of the CSS from AirportCards.vue and add it into this new component.

      Create a <style> tag and add in the following CSS:

      sfc-project/src/components/Card.vue

      <style>
      .card {
        border: 3px solid;
        border-radius: .5rem;
        padding: 1rem;
        margin-bottom: 1rem;
      }
      </style>
      

      Next, create the HTML template for this component. Before the <style> tag, add a <template> tag with the following:

      sfc-project/src/components/Card.vue

      <template>
        <div class="card">
      
        </div>
      </template>
      ...
      

      In between the <div class="card">, add the <slot /> component. This is a component that is provided to you by Vue. There is no need to import this component; it is globally imported by Vue.js:

      sfc-project/src/components/Card.vue

      <template>
        <div class="card">
          <slot />
        </div>
      </template>
      

      This slot is a placeholder for the HTML that is between the Card.vue component’s tags when it is referenced elsewhere.

      Save and exit the file.

      Now go back to the AirportCards.vue component. First, import the new Card.vue SFC you just created:

      sfc-project/src/components/AirportCards.vue

      ...
      <script>
      import Card from '@/components/Card.vue'
      
      export default {
        name: 'Airports',
        props: { ... },
        components: {
          Card
        }
      }
      </script>
      ...
      

      Now all that is left is to replace the <div> with <card>:

      /src/components/AirportCards.vue

      <template>
        <div class="wrapper">
          <card v-for="airport in airports" :key="airport.abbreviation">
            <p>{{ airport.abbreviation }}</p>
            <p>{{ airport.name }}</p>
            <p>{{ airport.city }}, {{ airport.state }}</p>
          </card>
        </div>
      </template>
      ...
      

      Since you have a <slot /> in your Card.vue component, the HTML between the <card> tags is injected in its place while preserving all styles that have been associated with a card.

      Save the file. When you open your browser at localhost:8080, you will find the same cards that you’ve had previously. The difference now is that your AirportCards.vue now reference the Card.vue component:

      US airport data rendered as cards, followed by European airport data rendered in the same way.

      To show the power of slots, open the App.vue component in your application and import the Card.vue component:

      sfc-project/src/App.vue

      ...
      <script>
      ...
      import Card from '@/components/Card.vue'
      
      export default {
        ...
        components: {
          AirportCards,
          Card
        },
        setup() { ... }
      }
      </script>
      ...
      

      In the <template>, add the following under the <airport-cards /> instances:

      sfc-project/src/App.vue

      <template>
        <AirportCards :airports="usAirports"/>
        <airport-cards :airports="euAirports" />
        <card>
          <p>US Airports</p>
          <p>Total: {{ usAirports.length }}</p>
        </card>
        <card>
          <p>EU Airports</p>
          <p>Total: {{ euAirports.length }}</p>
        </card>
      </template>
      ...
      

      Save the file and visit localhost:8080 in the browser. Your browser will now render additional elements displaying the number of airports in the datasets:

      US and European airports displayed, along with two cards that display that there are three airports in each dataset

      The HTML between the <card /> tags is not exactly the same, but it still renders a generic card. When leveraging slots, you can use this functionality to create small, re-usable components that have a number of different uses.

      Conclusion

      In this tutorial, you created single-file components and used props and slots to create reusable blocks of code. In the project, you created an AirportCards.vue component that renders a number of airport cards. You then broke up the AirportCards.vue component further into a Card.vue component with a default slot.

      You ended up with a number of components that are dynamic and can be used in a number of different uses, all while keeping code maintainable and in keeping with the D.R.Y. software principle.

      To learn more about Vue components, it is recommended to ready through the Vue documentation. For more tutorials on Vue, check out the How To Develop Websites with Vue.js series page.



      Source link

      How To Deploy Multiple Environments in Your Terraform Project Without Duplicating Code


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Terraform offers advanced features that become increasingly useful as your project grows in size and complexity. It’s possible to alleviate the cost of maintaining complex infrastructure definitions for multiple environments by structuring your code to minimize repetitions and introducing tool-assisted workflows for easier testing and deployment.

      Terraform associates a state with a backend, which determines where and how state is stored and retrieved. Every state has only one backend and is tied to an infrastructure configuration. Certain backends, such as local or s3, may contain multiple states. In that case, the pairing of state and infrastructure to the backend is describing a workspace. Workspaces allow you to deploy multiple distinct instances of the same infrastructure configuration without storing them in separate backends.

      In this tutorial, you’ll first deploy multiple infrastructure instances using different workspaces. You’ll then deploy a stateful resource, which, in this tutorial, will be a DigitalOcean Volume. Finally, you’ll reference pre-made modules from the Terraform Registry, which you can use to supplement your own.

      Prerequisites

      • A DigitalOcean Personal Access Token, which you can create via the DigitalOcean Control Panel. You can find instructions for this in the How to Generate a Personal Access Token tutorial.
      • Terraform installed on your local machine and a project set up with the DO provider. Complete Step 1 and Step 2 of the How To Use Terraform with DigitalOcean tutorial, and be sure to name the project folder terraform-advanced, instead of loadbalance. During Step 2, do not include the pvt_key variable and the SSH key resource.

      Note: We have specifically tested this tutorial using Terraform 0.13.

      Deploying Multiple Infrastructure Instances Using Workspaces

      Multiple workspaces are useful when you want to deploy or test a modified version of your main infrastructure without creating a separate project and setting up authentication keys again. Once you have developed and tested a feature using the separate state, you can incorporate the new code into the main workspace and possibly delete the additional state. When you init a Terraform project, regardless of backend, Terraform creates a workspace called default. It is always present and you can never delete it.

      However, multiple workspaces are not a suitable solution for creating multiple environments, such as for staging and production. Therefore workspaces, which only track the state, do not store the code or its modifications.

      Since workspaces do not track the actual code, you should manage the code separation between multiple workspaces at the version control (VCS) level by matching them to their infrastructure variants. How you can achieve this is dependent on the VCS tool itself; for example, in Git branches would be a fitting abstraction. To make it easier to manage the code for multiple environments, you can break them up into reusable modules, so that you avoid repeating similar code for each environment.

      Deploying Resources in Workspaces

      You’ll now create a project that deploys a Droplet, which you’ll apply from multiple workspaces.

      You’ll store the Droplet definition in a file called droplets.tf.

      Assuming you’re in the terraform-advanced directory, create and open it for editing by running:

      Add the following lines:

      droplets.tf

      resource "digitalocean_droplet" "web" {
        image  = "ubuntu-18-04-x64"
        name   = "web-${terraform.workspace}"
        region = "fra1"
        size   = "s-1vcpu-1gb"
      }
      

      This definition will create a Droplet running Ubuntu 18.04 with one CPU core and 1 GB RAM in the fra1 region. Its name will contain the name of the current workspace it is deployed from. When you’re done, save and close the file.

      Apply the project for Terraform to run its actions with:

      • terraform apply -var "do_token=${DO_PAT}"

      Your output will be similar to the following:

      Output

      An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.web will be created + resource "digitalocean_droplet" "web" { + backups = false + created_at = (known after apply) + disk = (known after apply) + id = (known after apply) + image = "ubuntu-18-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + ipv6_address_private = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "web-default" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "fra1" + resize_disk = true + size = "s-1vcpu-1gb" + status = (known after apply) + urn = (known after apply) + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. ...

      Enter yes when prompted to deploy the Droplet in the default workspace.

      The name of the Droplet will be web-default, because the workspace you start with is called default. You can list the workspaces to confirm that it’s the only one available:

      You’ll receive the following output:

      Output

      * default

      The asterisk (*) means that you currently have that workspace selected.

      Create and switch to a new workspace called testing, which you’ll use to deploy a different Droplet, by running workspace new:

      • terraform workspace new testing

      You’ll have output similar to:

      Output

      Created and switched to workspace "testing"! You're now on a new, empty workspace. Workspaces isolate their state, so if you run "terraform plan" Terraform will not see any existing state for this configuration.

      You plan the deployment of the Droplet again by running:

      • terraform plan -var "do_token=${DO_PAT}"

      The output will be similar to the previous run:

      Output

      An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.web will be created + resource "digitalocean_droplet" "web" { + backups = false + created_at = (known after apply) + disk = (known after apply) + id = (known after apply) + image = "ubuntu-18-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + ipv6_address_private = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "web-testing" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "fra1" + resize_disk = true + size = "s-1vcpu-1gb" + status = (known after apply) + urn = (known after apply) + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. ...

      Notice that Terraform plans to deploy a Droplet called web-testing, which it has named differently from web-default. This is because the default and testing workspaces have separate states and have no knowledge of each other’s resources—even though they stem from the same code.

      To confirm that you’re in the testing workspace, output the current one you’re in with workspace show:

      The output will be the name of the current workspace:

      Output

      testing

      To delete a workspace, you first need to destroy all its deployed resources. Then, if it’s active, you need to switch to another one using workspace select. Since the testing workspace here is empty, you can switch to default right away:

      • terraform workspace select default

      You’ll receive output of Terraform confirming the switch:

      Output

      Switched to workspace "default".

      You can then delete it by running workspace delete:

      • terraform workspace delete testing

      Terraform will then perform the deletion:

      Output

      Deleted workspace "testing"!

      You can destroy the Droplet you’ve deployed in the default workspace by running:

      • terraform destroy -var "do_token=${DO_PAT}"

      Enter yes when prompted to finish the process.

      In this section, you’ve worked in multiple Terraform workspaces. In the next section, you’ll deploy a stateful resource.

      Deploying Stateful Resources

      Stateless resources do not store data, so you can create and replace them quickly, because they are not unique. Stateful resources, on the other hand, contain data that is unique or not simply re-creatable; therefore, they require persistent data storage.

      Since you may end up destroying such resources, or multiple resources require their data, it’s best to store it in a separate entity, such as DigitalOcean Volumes.

      Volumes are objects that you can attach to Droplets (servers), but are separate from them, and provide additional storage space. In this step, you’ll define the Volume and connect it to a Droplet in droplets.tf.

      Open it for editing:

      Add the following lines:

      droplets.tf

      resource "digitalocean_droplet" "web" {
        image  = "ubuntu-18-04-x64"
        name   = "web-${terraform.workspace}"
        region = "fra1"
        size   = "s-1vcpu-1gb"
      }
      
      resource "digitalocean_volume" "volume" {
        region                  = "fra1"
        name                    = "new-volume"
        size                    = 10
        initial_filesystem_type = "ext4"
        description             = "New Volume for Droplet"
      }
      
      resource "digitalocean_volume_attachment" "volume_attachment" {
        droplet_id = digitalocean_droplet.web.id
        volume_id  = digitalocean_volume.volume.id
      }
      

      Here you define two new resources, the Volume itself and a Volume attachment. The Volume will be 10GB, formatted as ext4, called new-volume, and located in the same region as the Droplet. To connect the Volume to the Droplet, since they are separate entities, you define a Volume attachment object. volume_attachment takes the Droplet and Volume IDs and instructs the DigitalOcean cloud to make the Volume available to the Droplet as a disk device.

      When you’re done, save and close the file.

      Plan this configuration by running:

      • terraform plan -var "do_token=${DO_PAT}"

      The actions that Terraform will plan will be the following:

      Output

      An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.web will be created + resource "digitalocean_droplet" "web" { + backups = false + created_at = (known after apply) + disk = (known after apply) + id = (known after apply) + image = "ubuntu-18-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + ipv6_address_private = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "web-default" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "fra1" + resize_disk = true + size = "s-1vcpu-1gb" + status = (known after apply) + urn = (known after apply) + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } # digitalocean_volume.volume will be created + resource "digitalocean_volume" "volume" { + description = "New Volume for Droplet" + droplet_ids = (known after apply) + filesystem_label = (known after apply) + filesystem_type = (known after apply) + id = (known after apply) + initial_filesystem_type = "ext4" + name = "new-volume" + region = "fra1" + size = 10 + urn = (known after apply) } # digitalocean_volume_attachment.volume_attachment will be created + resource "digitalocean_volume_attachment" "volume_attachment" { + droplet_id = (known after apply) + id = (known after apply) + volume_id = (known after apply) } Plan: 3 to add, 0 to change, 0 to destroy. ...

      The output details that Terraform would create a Droplet, a Volume, and a Volume attachment, which connects the Volume to the Droplet.

      You’ve now defined and connected a Volume (a stateful resource) to a Droplet. In the next section, you’ll review public, pre-made Terraform modules that you can incorporate in your project.

      Referencing Pre-made Modules

      Aside from creating your own custom modules for your projects, you can also use pre-made modules and providers from other developers, which are publicly available at Terraform Registry.

      In the modules section you can search the database of available modules and sort by provider in order to find the module with the functionality you need. Once you’ve found one, you can read its description, which lists the inputs and outputs the module provides, as well as its external module and provider dependencies.

      Terraform Registry - SSH key Module

      You’ll now add the DigitalOcean SSH key module to your project. You’ll store the code separate from existing definitions in a file called ssh-key.tf. Create and open it for editing by running:

      Add the following lines:

      ssh-key.tf

      module "ssh-key" {
        source         = "clouddrove/ssh-key/digitalocean"
        key_path       = "~/.ssh/id_rsa.pub"
        key_name       = "new-ssh-key"
        enable_ssh_key = true
      }
      

      This code defines an instance of the clouddrove/droplet/digitalocean module from the registry and sets some of the parameters it offers. It should add a public SSH key to your account by reading it from the ~/.ssh/id_rsa.pub.

      When you’re done, save and close the file.

      Before you plan this code, you must download the referenced module by running:

      You’ll receive output similar to the following:

      Output

      Initializing modules... Downloading clouddrove/ssh-key/digitalocean 0.13.0 for ssh-key... - ssh-key in .terraform/modules/ssh-key Initializing the backend... Initializing provider plugins... - Using previously-installed digitalocean/digitalocean v1.22.2 Terraform has been successfully initialized! ...

      You can now plan the code for the changes:

      • terraform plan -var "do_token=${DO_PAT}"

      You’ll receive output similar to this:

      Output

      Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: ... # module.ssh-key.digitalocean_ssh_key.default[0] will be created + resource "digitalocean_ssh_key" "default" { + fingerprint = (known after apply) + id = (known after apply) + name = "devops" + public_key = "ssh-rsa ... demo@clouddrove" } Plan: 4 to add, 0 to change, 0 to destroy. ...

      The output shows that you would create the SSH key resource, which means that you downloaded and invoked the module from your code.

      Conclusion

      Bigger projects can make use of some advanced features Terraform offers to help reduce complexity and make maintenance easier. Workspaces allow you to test new additions to your code without touching the stable main deployments. You can also couple workspaces with a version control system to track code changes. Using pre-made modules can also shorten development time, but may incur additional expenses or time in the future if the module becomes obsolete.

      For further resources on using Terraform, check out our How To Manage Infrastructure With Terraform series.



      Source link