Setting Template Properties and Using Environment Variables

Templates also support the use of custom template properties and predefined environment variables so that you can reuse templates across zones, regions, or even projects.

For example, you can use an arbitrary template property in place of hard coding a specific zone value in your template. Anytime you use the template, you include the template and provide a value for the zone in the top-level configuration.

Reference an arbitrary template property using this syntax in your templates:

Jinja

{{ properties["property-name"] }}

Python

context.properties["property-name"]

For example, the following template uses the following template properties: zone, machineType, and network:

Jinja

resources:
- name: 'example'
  type: compute.v1.instance
  properties:
    zone: {{ properties["zone"] }}
    machineType: https://www.googleapis.com/compute/v1/projects/my-project/zones/{{ properties["zone"] }}/machineTypes/{{ properties["machineType"] }}
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-8
    networkInterfaces:
    - network: $(ref.{{ properties["network"] }}.selfLink)
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

Python

"""Creates the virtual machine."""

def GenerateConfig(context):
  """Creates the virtual machine with environment variables."""

  resources = [{
      'name': 'example',
      'type': 'compute.v1.instance',
      'properties': {
          'zone': context.properties['zone'],
          'machineType': ''.join(['https://www.googleapis.com/compute/v1/',
                                  'projects/my-project/zones/',
                                  context.properties['zone'], '/machineTypes/',
                                  context.properties['machineType']]),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage': ''.join(['https://www.googleapis.com/compute/v1/', 'projects/',
                                          'debian-cloud/global/',
                                          'images/family/debian-8'])
              }
          }],
          'networkInterfaces': [{
              'network': '$(ref.' + context.properties['network']
                         + '.selfLink)',
              'accessConfigs': [{
                  'name': 'External NAT',
                  'type': 'ONE_TO_ONE_NAT'
              }]
          }]
      }
  }]
  return {'resources': resources}

Then you would set the value of this template property at the top-level configuration or the parent template:

resources:
- name: the-first-vm
  type: vm-template.jinja
  properties:
    machineType: f1-micro # Sets the 'machineType' template property
    zone: us-central1-f # Sets the 'zone' template property
    network: example-network # Sets the 'network' template property
- name: the-second-vm
  ...

In addition to template properties, you can also reference environment variables that are prepopulated with specific information about your deployment. Valid environment variables include the deployment name, the project ID, the name property of your resource, and the type of your configuration. For more details about each environment variable, read the Environment Variables documentation.

You can reference an environment variable using this syntax:

Jinja

{{ env["variable-name"] }}

Python

context.env['variable-name']

For example, the following template uses the name and project environment variables.

Jinja

{#
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#}

resources:
- name: {{ env["name"] }}
  type: compute.v1.instance
  properties:
    zone: {{ properties["zone"] }}
    machineType: https://www.googleapis.com/compute/v1/projects/{{ env["project"] }}/zones/{{ properties["zone"] }}/machineTypes/{{ properties["machineType"] }}
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-9
    networkInterfaces:
    - network: $(ref.{{ properties["network"] }}.selfLink)
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

Python

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates the virtual machine."""

COMPUTE_URL_BASE = 'https://www.googleapis.com/compute/v1/'


def GenerateConfig(context):
  """Creates the virtual machine with environment variables."""

  resources = [{
      'name': context.env['name'],
      'type': 'compute.v1.instance',
      'properties': {
          'zone': context.properties['zone'],
          'machineType': ''.join([COMPUTE_URL_BASE, 'projects/',
                                  context.env['project'], '/zones/',
                                  context.properties['zone'], '/machineTypes/',
                                  context.properties['machineType']]),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage': ''.join([COMPUTE_URL_BASE, 'projects/',
                                          'debian-cloud/global/',
                                          'images/family/debian-9'])
              }
          }],
          'networkInterfaces': [{
              'network': '$(ref.' + context.properties['network']
                         + '.selfLink)',
              'accessConfigs': [{
                  'name': 'External NAT',
                  'type': 'ONE_TO_ONE_NAT'
              }]
          }]
      }
  }]
  return {'resources': resources}

Let's convert the templates you have to take advantage of template properties and environment variables.

Edit the vm-template.jinja file

Open your vm-template file and replace the following parts of the file with the appropriate template properties and environment variables.

  1. Replace the-first-vm with:

    Jinja

    {{ env["name"] }}
    

    Python

    context.env['name']
    

  2. Replace all occurrences of your project ID with:

    Jinja

    {{ env["project"] }}
    

    Python

    context.env['project']
    

  3. Next, replace the zone. Replace all occurrences of us-central1-f with:

    Jinja

    {{ properties["zone"] }}
    

    Python

    context.properties['zone']
    

  4. Replace f1-micro with:

    Jinja

    {{ properties["machineType"] }}
    

    Python

    context.properties["machineType"]
    

  5. Replace $(ref.a-new-network.selfLink) with:

    Jinja

    $(ref.{{ properties["network"] }}.selfLink)
    

    Python

    '$(ref.' + context.properties['network'] + '.selfLink)'
    

As a sanity check, make sure your vm-template.jinja file looks like this:

Jinja

{#
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#}

resources:
- name: {{ env["name"] }}
  type: compute.v1.instance
  properties:
    zone: {{ properties["zone"] }}
    machineType: https://www.googleapis.com/compute/v1/projects/{{ env["project"] }}/zones/{{ properties["zone"] }}/machineTypes/{{ properties["machineType"] }}
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-9
    networkInterfaces:
    - network: $(ref.{{ properties["network"] }}.selfLink)
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

Python

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates the virtual machine."""

COMPUTE_URL_BASE = 'https://www.googleapis.com/compute/v1/'


def GenerateConfig(context):
  """Creates the virtual machine with environment variables."""

  resources = [{
      'name': context.env['name'],
      'type': 'compute.v1.instance',
      'properties': {
          'zone': context.properties['zone'],
          'machineType': ''.join([COMPUTE_URL_BASE, 'projects/',
                                  context.env['project'], '/zones/',
                                  context.properties['zone'], '/machineTypes/',
                                  context.properties['machineType']]),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage': ''.join([COMPUTE_URL_BASE, 'projects/',
                                          'debian-cloud/global/',
                                          'images/family/debian-9'])
              }
          }],
          'networkInterfaces': [{
              'network': '$(ref.' + context.properties['network']
                         + '.selfLink)',
              'accessConfigs': [{
                  'name': 'External NAT',
                  'type': 'ONE_TO_ONE_NAT'
              }]
          }]
      }
  }]
  return {'resources': resources}

Edit the network-template.jinja template

Edit the network-template.jinja file and replace a-new-network with:

Jinja

{{ env["name"] }}

Python

context.env['name']

As a sanity check, your network template should look like this:

Jinja

{#
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#}

resources:
- name: {{ env["name"] }}
  type: compute.v1.network
  properties:
    routingConfig:
      routingMode: REGIONAL
    autoCreateSubnetworks: true

Python

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates the network."""


def GenerateConfig(context):
  """Creates the network with environment variables."""

  resources = [{
      'name': context.env['name'],
      'type': 'compute.v1.network',
      'properties': {
          'routingConfig': {
              'routingMode': 'REGIONAL'
          },
          'autoCreateSubnetworks': True
      }
  }]
  return {'resources': resources}

Save your changes.

Edit the firewall-template.jinja template

  1. Replace the firewall name with:

    Jinja

    {{ env["name"] }}
    

    Python

    context.env['name']
    

  2. Replace $(ref.a-new-network.selfLink) with:

    Jinja

    $(ref.{{ properties["network"] }}.selfLink)
    

    Python

    '$(ref.' + context.properties['network'] + '.selfLink)'
    

As a sanity check, your firewall template should look like this:

Jinja

{#
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#}

resources:
- name: {{ env["name"] }}
  type: compute.v1.firewall
  properties:
    network: $(ref.{{ properties["network"] }}.selfLink)
    sourceRanges: ["0.0.0.0/0"]
    allowed:
    - IPProtocol: TCP
      ports: ["80"]

Python

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates the firewall."""


def GenerateConfig(context):
  """Creates the firewall with environment variables."""

  resources = [{
      'name': context.env['name'],
      'type': 'compute.v1.firewall',
      'properties': {
          'network': '$(ref.' + context.properties['network'] + '.selfLink)',
          'sourceRanges': ['0.0.0.0/0'],
          'allowed': [{
              'IPProtocol': 'TCP',
              'ports': [80]
          }]
      }
  }]
  return {'resources': resources}

Save your changes.

Edit the compute-engine-template.jinja template

Update compute-engine-template.[jinja|py] so it contains new virtual machine instance names using the vm-template.[jinja|py] template. Noticed that you have completely removed vm-template-2.jinja, simplifying all of the dynamic properties within a single template that you can reuse.

Jinja

{#
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#}

{% set  NETWORK_NAME = "once-upon-a-custom-network" %}

resources:
- name: the-first-vm
  type: vm-template.jinja
  properties:
    machineType: f1-micro
    zone: us-central1-f
    network: {{ NETWORK_NAME }}
- name: the-second-vm
  type: vm-template.jinja
  properties:
    machineType: g1-small
    zone: us-central1-f
    network: {{ NETWORK_NAME }}
- name: {{ NETWORK_NAME }}
  type: network-template.jinja
- name: {{ NETWORK_NAME }}-firewall
  type: firewall-template.jinja
  properties:
    network: {{ NETWORK_NAME }}

Python

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates the Compute Engine."""

NETWORK_NAME = 'once-upon-a-custom-network'


def GenerateConfig(unused_context):
  """Creates the Compute Engine with multiple templates."""

  resources = [{
      'name': 'the-first-vm',
      'type': 'vm-template.py',
      'properties': {
          'machineType': 'f1-micro',
          'zone': 'us-central1-f',
          'network': NETWORK_NAME
      }
  }, {
      'name': 'the-second-vm',
      'type': 'vm-template.py',
      'properties': {
          'machineType': 'f1-micro',
          'zone': 'us-central1-f',
          'network': NETWORK_NAME
      }
  }, {
      'name': NETWORK_NAME,
      'type': 'network-template.py'
  }, {
      'name': NETWORK_NAME + '-firewall',
      'type': 'firewall-template.py',
      'properties': {
          'network': NETWORK_NAME
      }
  }]
  return {'resources': resources}

Since you added the name environment variable in the base template, Deployment Manager names the resources using the name defined in the parent template (the template calling the base template). The parent template in this case would be compute-engine-template.[jinja|py] so the virtual machine instance names would be the-first-vm and the-second-vm. You do not have to define your project variable because Deployment Manager automatically recognizes it as an environment variable and populates the value automatically.

Although compute-engine-template.[jinja|py] is now longer and a bit more complex, it is also much more dynamic. Using a combination of template and environment variables, you could reuse this template to create a complete Compute Engine cluster, customizing machine types, zone specification, and network name each time you reuse the template. This eliminates creating multiple copies of the same configuration for deployments that have the same setup.

Update your configuration file

Next, edit your configuration file, config-with-many-templates.yaml, and remove the line importing vm-template-2.[jinja|py], so your configuration now looks like this:

Jinja

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

imports:
- path: vm-template.jinja
- path: network-template.jinja
- path: firewall-template.jinja
- path: compute-engine-template.jinja

resources:
- name: compute-engine-setup
  type: compute-engine-template.jinja

Python

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

imports:
- path: vm-template.py
- path: network-template.py
- path: firewall-template.py
- path: compute-engine-template.py

resources:
- name: compute-engine-setup
  type: compute-engine-template.py

Save your changes and, if you want, you can redeploy your configuration to confirm the variables work.

gcloud deployment-manager deployments create deployment-with-many-templates --config config-with-many-templates.yaml

Next, let's discuss how to add metadata and startup scripts to your template.

Delete Your Deployment

Compute Engine resources incur charges, so you should delete this deployment, as it is no longer necessary for future steps. Deleting this deployment deletes all the resources in this deployment as well. If you don’t delete the deployment, you will run into conflicts with future examples.

To delete this deployment:

gcloud deployment-manager deployments delete deployment-with-many-templates

Next: Set Metadata and Startup Scripts

Related topics

Var denne siden nyttig? Si fra hva du synes:

Send tilbakemelding om ...

Cloud Deployment Manager Documentation