[Yandex Cloud documentation](../../../index.md) > [Yandex Managed Service for YDB](../../index.md) > [Tutorials](../index.md) > [Converting a video to a GIF in Python](index.md) > Terraform

# Converting a video to a GIF in Python using Terraform

To create a [video-to-GIF conversion framework in Python](index.md) using Terraform:

1. [Get your cloud ready](#before-begin).
1. [Create the infrastructure](#deploy).
1. [Create a table](#create-table).
1. [Test the application](#test-app).


## Get your cloud ready {#before-begin}

Sign up for Yandex Cloud and create a [billing account](../../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../../billing/quickstart/index.md) and [link](../../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../../resource-manager/concepts/resources-hierarchy.md).


### Required paid resources {#paid-resources}

The infrastructure support cost includes:
* Fee for invoking [functions](../../../functions/concepts/function.md) (see [Yandex Cloud Functions pricing](../../../functions/pricing.md)).
* Fee for running queries to the [database](../../concepts/serverless-and-dedicated.md) (see [Yandex Managed Service for YDB pricing](../../pricing/serverless.md)).
* Fee for storing data in a [bucket](../../../storage/concepts/bucket.md) (see [Yandex Object Storage pricing](../../../storage/pricing.md)).


## Create an infrastructure {#deploy}

With [Terraform](https://www.terraform.io/), you can quickly create a cloud infrastructure in Yandex Cloud and manage it using configuration files. These files store the infrastructure description written in HashiCorp Configuration Language (HCL). If you change the configuration files, Terraform automatically detects which part of your configuration is already deployed, and what should be added or removed.

Terraform is distributed under the [Business Source License](https://github.com/hashicorp/terraform/blob/main/LICENSE). The [Yandex Cloud provider for Terraform](https://github.com/yandex-cloud/terraform-provider-yandex) is distributed under the [MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/) license.

For more information about the provider resources, see the guides on the [Terraform](https://www.terraform.io/docs/providers/yandex/index.html) website or [its mirror](../../../terraform/index.md).

To create your infrastructure via Terraform:
1. [Install Terraform](../../../tutorials/infrastructure-management/terraform-quickstart.md#install-terraform), [get authentication credentials](../../../tutorials/infrastructure-management/terraform-quickstart.md#get-credentials), and specify the source for installing the Yandex Cloud provider. For details, see [Configure your provider](../../../tutorials/infrastructure-management/terraform-quickstart.md#configure-provider), step 1.
1. Prepare your infrastructure description files:

   {% list tabs group=infrastructure_description %}

   - Ready-made configuration {#ready}

     1. Clone the repository containing the configuration files.

        ```bash
        git clone https://github.com/yandex-cloud-examples/yc-serverless-video-gif-converter.git
        ```

     1. Navigate to the repository directory. It should now contain the following files:
        * `video-converting.tf`: New infrastructure configuration.
        * `ffmpeg-api.zip`: API function archive.
        * `src.zip`: Converter function archive.

   - Manually {#manual}

     1. Create a folder for configuration files.
     1. In the folder, create:
        1. `video-converting.tf` configuration file:

           {% cut "video-converting.tf" %}

           ```hcl
           # Declaring variables
           
           locals {
             folder_id   = "<folder_ID>"
             bucket_name = "<bucket_name>"
           }
           
           # Configuring the provider
           
           terraform {
             required_providers {
               yandex = {
                 source  = "yandex-cloud/yandex"
               }
             }
           }
           
           provider "yandex" {
             folder_id = local.folder_id
           }
           
           # Creating a service account and assigning roles to it
           
           resource "yandex_iam_service_account" "sa" {
             name = "ffmpeg-sa"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "ymq-reader" {
             folder_id = local.folder_id
             role      = "ymq.reader"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "ymq-writer" {
             folder_id = local.folder_id
             role      = "ymq.writer"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "lockbox-payload-viewer" {
             folder_id = local.folder_id
             role      = "lockbox.payloadViewer"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "storage-editor" {
             folder_id = local.folder_id
             role      = "storage.editor"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "storage-uploader" {
             folder_id = local.folder_id
             role      = "storage.uploader"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "ydb-admin" {
             folder_id = local.folder_id
             role      = "ydb.admin"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           resource "yandex_resourcemanager_folder_iam_member" "serverless-functions-invoker" {
             folder_id = local.folder_id
             role      = "serverless.functions.invoker"
             member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
           }
           
           # Creating a static key for the service account
           
           resource "yandex_iam_service_account_static_access_key" "sa-static-key" {
             service_account_id = yandex_iam_service_account.sa.id
             description        = "static access key for database, message queue and bucket"
           }
           
           # Creating a secret
           
           resource "yandex_lockbox_secret" "secretmq" {
             name = "ffmpeg-sa-secret"
           }
           
           resource "yandex_lockbox_secret_version" "my_version" {
             secret_id = yandex_lockbox_secret.secretmq.id
             entries {
               key        = "ACCESS_KEY_ID"
               text_value = yandex_iam_service_account_static_access_key.sa-static-key.access_key
             }
               entries {
               key        = "SECRET_ACCESS_KEY"
               text_value = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
             }
           }
           
           # Creating a message queue
           
           resource "yandex_message_queue" "converter_queue" {
             name                       = "converter-queue"
             visibility_timeout_seconds = 600
             message_retention_seconds  = 1209600
             receive_wait_time_seconds  = 20
           
             access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key
             secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
             depends_on = [ yandex_resourcemanager_folder_iam_member.ymq-writer ]
           }
           
           # Creating a database
           
           resource "yandex_ydb_database_serverless" "api_db" {
             name        = "db-converter"
             location_id = "ru-central1"
           }
           
           # Creating a bucket and uploading an archive
           
           resource "yandex_storage_bucket" "conv_func_bucket" {
             folder_id  = local.folder_id
             access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key
             secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
             bucket     = local.bucket_name
           }
           
           resource "yandex_storage_object" "archive" {
             access_key   = yandex_iam_service_account_static_access_key.sa-static-key.access_key
             secret_key   = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
             bucket       = yandex_storage_bucket.conv_func_bucket.id
             key          = "src.zip"
             source       = "src.zip"
             content_type = "application/zip"
           }
           
           # Creating an API function
           
           resource "yandex_function" "api-function" {
             name               = "ffmpeg-api"
             runtime            = "python312"
             user_hash          = filesha256("ffmpeg-api.zip")
             memory             = "256"
             entrypoint         = "index.handle_api"
             execution_timeout  = "5"
             service_account_id = yandex_iam_service_account.sa.id
             environment = {
               DOCAPI_ENDPOINT = yandex_ydb_database_serverless.api_db.document_api_endpoint
               YMQ_QUEUE_URL   = yandex_message_queue.converter_queue.id
               SECRET_ID       = yandex_lockbox_secret.secretmq.id
             }
             content {
               zip_filename = "ffmpeg-api.zip"
             }
           }
           
           # Creating a converter function
           
           resource "yandex_function" "converter" {
             name               = "ffmpeg-converter"
             runtime            = "python312"
             user_hash          = filesha256("src.zip")
             memory             = "2048"
             entrypoint         = "index.handle_process_event"
             execution_timeout  = "600"
             service_account_id = yandex_iam_service_account.sa.id
             environment = {
               DOCAPI_ENDPOINT = yandex_ydb_database_serverless.api_db.document_api_endpoint
               YMQ_QUEUE_URL   = yandex_message_queue.converter_queue.id
               S3_BUCKET       = yandex_storage_bucket.conv_func_bucket.id
               SECRET_ID       = yandex_lockbox_secret.secretmq.id
             }
             package {
               bucket_name = yandex_storage_bucket.conv_func_bucket.id
               object_name = "src.zip"
             }
           }
           
           # Creating a trigger
           
           resource "yandex_function_trigger" "converter_trigger" {
             name = "ffmpeg-trigger"
             message_queue {
               queue_id           = yandex_message_queue.converter_queue.arn
               service_account_id = yandex_iam_service_account.sa.id
               batch_size         = "1"
               batch_cutoff       = "10"
               visibility_timeout = 600
             }
             function {
               id                 = yandex_function.converter.id
               tag                = "$latest"
               service_account_id = yandex_iam_service_account.sa.id
             }
           }
           ```

           {% endcut %}

        1. For an API function:
           1. Create a file named `index.py` and paste this content into it:

              {% cut "index.py for an API function" %}

              ```python
              import json
              import os
              import subprocess
              import uuid
              from urllib.parse import urlencode
              
              import boto3
              import requests
              import yandexcloud
              from yandex.cloud.lockbox.v1.payload_service_pb2 import GetPayloadRequest
              from yandex.cloud.lockbox.v1.payload_service_pb2_grpc import PayloadServiceStub
              
              boto_session = None
              storage_client = None
              docapi_table = None
              ymq_queue = None
              
              
              def get_boto_session():
                  global boto_session
                  if boto_session is not None:
                      return boto_session
              
                  # initialize lockbox and read secret value
                  yc_sdk = yandexcloud.SDK()
                  channel = yc_sdk._channels.channel("lockbox-payload")
                  lockbox = PayloadServiceStub(channel)
                  response = lockbox.Get(GetPayloadRequest(secret_id=os.environ['SECRET_ID']))
              
                  # extract values from secret
                  access_key = None
                  secret_key = None
                  for entry in response.entries:
                      if entry.key == 'ACCESS_KEY_ID':
                          access_key = entry.text_value
                      elif entry.key == 'SECRET_ACCESS_KEY':
                          secret_key = entry.text_value
                  if access_key is None or secret_key is None:
                      raise Exception("secrets required")
                  print("Key id: " + access_key)
              
                  # initialize boto session
                  boto_session = boto3.session.Session(
                      aws_access_key_id=access_key,
                      aws_secret_access_key=secret_key
                  )
                  return boto_session
              
              
              def get_ymq_queue():
                  global ymq_queue
                  if ymq_queue is not None:
                      return ymq_queue
              
                  ymq_queue = get_boto_session().resource(
                      service_name='sqs',
                      endpoint_url='https://message-queue.api.cloud.yandex.net',
                      region_name='ru-central1'
                  ).Queue(os.environ['YMQ_QUEUE_URL'])
                  return ymq_queue
              
              
              def get_docapi_table():
                  global docapi_table
                  if docapi_table is not None:
                      return docapi_table
              
                  docapi_table = get_boto_session().resource(
                      'dynamodb',
                      endpoint_url=os.environ['DOCAPI_ENDPOINT'],
                      region_name='ru-central1'
                  ).Table('tasks')
                  return docapi_table
              
              
              def get_storage_client():
                  global storage_client
                  if storage_client is not None:
                      return storage_client
              
                  storage_client = get_boto_session().client(
                      service_name='s3',
                      endpoint_url='https://storage.yandexcloud.net',
                      region_name='ru-central1'
                  )
                  return storage_client
              
              # API handler
              
              def create_task(src_url):
                  task_id = str(uuid.uuid4())
                  get_docapi_table().put_item(Item={
                      'task_id': task_id,
                      'ready': False
                  })
                  get_ymq_queue().send_message(MessageBody=json.dumps({'task_id': task_id, "src": src_url}))
                  return {
                      'task_id': task_id
                  }
              
              
              def get_task_status(task_id):
                  task = get_docapi_table().get_item(Key={
                      "task_id": task_id
                  })
                  if task['Item']['ready']:
                      return {
                          'ready': True,
                          'gif_url': task['Item']['gif_url']
                      }
                  return {'ready': False}
              
              
              def handle_api(event, context):
                  action = event['action']
                  if action == 'convert':
                      return create_task(event['src_url'])
                  elif action == 'get_task_status':
                      return get_task_status(event['task_id'])
                  else:
                      return {"error": "unknown action: " + action}
              ```

              {% endcut %}

           1. Create a file named `requirements.txt` and specify these libraries in it:

               ```text
               boto3
               yandexcloud
               ```

           1. In the folder, create an archive named `ffmpeg-api.zip` containing the files `requirements.txt` and `index.py`.

        1. For a converter function:
           1. Create a file named `index.py` and paste this content into it:

              {% cut "index.py for a converter function" %}

              ```python
              import json
              import os
              import subprocess
              import uuid
              from urllib.parse import urlencode
              
              import boto3
              import requests
              import yandexcloud
              from yandex.cloud.lockbox.v1.payload_service_pb2 import GetPayloadRequest
              from yandex.cloud.lockbox.v1.payload_service_pb2_grpc import PayloadServiceStub
              
              boto_session = None
              storage_client = None
              docapi_table = None
              ymq_queue = None
              
              
              def get_boto_session():
                  global boto_session
                  if boto_session is not None:
                      return boto_session
              
                  # initialize lockbox and read secret value
                  yc_sdk = yandexcloud.SDK()
                  channel = yc_sdk._channels.channel("lockbox-payload")
                  lockbox = PayloadServiceStub(channel)
                  response = lockbox.Get(GetPayloadRequest(secret_id=os.environ['SECRET_ID']))
              
                  # extract values from secret
                  access_key = None
                  secret_key = None
                  for entry in response.entries:
                      if entry.key == 'ACCESS_KEY_ID':
                          access_key = entry.text_value
                      elif entry.key == 'SECRET_ACCESS_KEY':
                          secret_key = entry.text_value
                  if access_key is None or secret_key is None:
                      raise Exception("secrets required")
                  print("Key id: " + access_key)
              
                  # initialize boto session
                  boto_session = boto3.session.Session(
                      aws_access_key_id=access_key,
                      aws_secret_access_key=secret_key
                  )
                  return boto_session
              
              
              def get_ymq_queue():
                  global ymq_queue
                  if ymq_queue is not None:
                      return ymq_queue
              
                  ymq_queue = get_boto_session().resource(
                      service_name='sqs',
                      endpoint_url='https://message-queue.api.cloud.yandex.net',
                      region_name='ru-central1'
                  ).Queue(os.environ['YMQ_QUEUE_URL'])
                  return ymq_queue
              
              
              def get_docapi_table():
                  global docapi_table
                  if docapi_table is not None:
                      return docapi_table
              
                  docapi_table = get_boto_session().resource(
                      'dynamodb',
                      endpoint_url=os.environ['DOCAPI_ENDPOINT'],
                      region_name='ru-central1'
                  ).Table('tasks')
                  return docapi_table
              
              
              def get_storage_client():
                  global storage_client
                  if storage_client is not None:
                      return storage_client
              
                  storage_client = get_boto_session().client(
                      service_name='s3',
                      endpoint_url='https://storage.yandexcloud.net',
                      region_name='ru-central1'
                  )
                  return storage_client
              
              # Converter handler
              
              def download_from_ya_disk(public_key, dst):
                  api_call_url = 'https://cloud-api.yandex.net/v1/disk/public/resources/download?' + \
                                 urlencode(dict(public_key=public_key))
                  response = requests.get(api_call_url)
                  download_url = response.json()['href']
                  download_response = requests.get(download_url)
                  with open(dst, 'wb') as video_file:
                      video_file.write(download_response.content)
              
              
              def upload_and_presign(file_path, object_name):
                  client = get_storage_client()
                  bucket = os.environ['S3_BUCKET']
                  client.upload_file(file_path, bucket, object_name)
                  return client.generate_presigned_url('get_object', Params={'Bucket': bucket, 'Key': object_name}, ExpiresIn=3600)
              
              
              def handle_process_event(event, context):
                  for message in event['messages']:
                      task_json = json.loads(message['details']['message']['body'])
                      task_id = task_json['task_id']
                      # Download video
                      download_from_ya_disk(task_json['src'], '/tmp/video.mp4')
                      # Convert with ffmpeg
                      subprocess.run(['ffmpeg', '-i', '/tmp/video.mp4', '-r', '10', '-s', '320x240', '/tmp/result.gif'])
                      result_object = task_id + ".gif"
                      # Upload to Object Storage and generate presigned url
                      result_download_url = upload_and_presign('/tmp/result.gif', result_object)
                      # Update task status in DocAPI
                      get_docapi_table().update_item(
                          Key={'task_id': task_id},
                          AttributeUpdates={
                              'ready': {'Value': True, 'Action': 'PUT'},
                              'gif_url': {'Value': result_download_url, 'Action': 'PUT'},
                          }
                      )
                  return "OK"
              ```

              {% endcut %}

           1. Create a file named `requirements.txt` and specify these libraries in it:

               ```text
               boto3
               requests
               yandexcloud
               ```

           1. Prepare the FFmpeg executable. On [FFmpeg's official website](http://ffmpeg.org/download.html), navigate to the **Linux Static Builds** section, download the 64-bit FFmpeg archive, and make the file executable by running the `chmod +x ffmpeg` command.

           1. In the folder, create an archive named `src.zip` containing the files `requirements.txt` and `index.py`, and the FFmpeg executable.

   {% endlist %}

   For more on the properties of resources used in Terraform, see these provider guides:
   * [Service account](../../../iam/concepts/users/service-accounts.md): [yandex_iam_service_account](../../../terraform/resources/iam_service_account.md).
   * [Role](../../../iam/concepts/access-control/roles.md): [yandex_resourcemanager_folder_iam_member](../../../terraform/resources/resourcemanager_folder_iam_member.md).
   * [Secret](../../../terraform/resources/lockbox_secret.md): [yandex_lockbox_secret](../../../lockbox/concepts/secret.md).
   * [Secret version](../../../lockbox/concepts/secret.md#version): [yandex_lockbox_secret_version](../../../terraform/resources/lockbox_secret_version.md).
   * [Message queue](../../../message-queue/concepts/queue.md): [yandex_message_queue](../../../terraform/resources/message_queue.md).
   * [Database (YDB)](../../../message-queue/concepts/queue.md): [yandex_ydb_database_serverless](../../../terraform/resources/ydb_database_serverless.md).
   * [Bucket](../../../storage/concepts/bucket.md): [yandex_storage_bucket](../../../terraform/resources/storage_bucket.md)
   * [Bucket object](../../../storage/concepts/object.md): [yandex_storage_object](../../../terraform/resources/storage_object.md).
   * [Function](../../../functions/concepts/function.md): [yandex_function](../../../terraform/resources/function.md).
   * [Trigger](../../../functions/concepts/trigger/ymq-trigger.md): [yandex_function_trigger](../../../terraform/resources/function_trigger.md).

1. In the `video-converting.tf` file, set the following user-defined properties:
   * `folder_id`: [Folder ID](../../../resource-manager/operations/folder/get-id.md).
   * `bucket`: Bucket name.

1. Create the resources:

   1. In the terminal, navigate to the configuration file directory.
   1. Make sure the configuration is correct using this command:
   
      ```bash
      terraform validate
      ```
   
      If the configuration is valid, you will get this message:
   
      ```bash
      Success! The configuration is valid.
      ```
   
   1. Run this command:
   
      ```bash
      terraform plan
      ```
   
      You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
   1. Apply the configuration changes:
   
      ```bash
      terraform apply
      ```
   
   1. Type `yes` and press **Enter** to confirm the changes.

After you have created the infrastructure, [create a table](#create-table) in YDB.




## Create a table {#create-table}

1.  [Create a table](../../operations/schema.md#create-table)  in YDB:

    * **Name**: `tasks`.
    * **Table type**: [Document table](../../operations/schema.md#create-table).
    * **Columns**: One column named `task_id` of the `String` type. Set the [Partition key](../../operations/schema.md#create-table) attribute.

After you have created the table, [test the application](#test-app).



## Test the application {#test-app}

### Create a task {#create-task}

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder containing the `ffmpeg-api` function.
  1. Select **Cloud Functions**.
  1. Select the `ffmpeg-api` function.
  1. Navigate to the **Testing** tab.
  1. In the **Payload** field, enter:

     ```json
     {"action":"convert", "src_url":"<link_to_video>"}
     ```

     Where `<link_to_video>` is a link to an [MP4](https://en.wikipedia.org/wiki/MP4_file_format) video file saved to [Yandex Disk](https://disk.yandex.com).

  1. Click **Run test**.
  1. You will see the task ID in the **Function output** field:

     ```json
     { "task_id": "c4269ceb-8d3a-40fe-95f0-84cf********" }
     ```

{% endlist %}

### View the queue statistics {#ymq-stat}

Once the task is created, the queued message count will increase by one and a trigger will fire. Make sure messages arrive in the queue and are handled. To do this, view the queue statistics.

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder housing `converter-queue`.
  1. Select **Message Queue**.
  1. Select `converter-queue`.
  1. Under **General information**, you can see the queued and processed message counts.
  1. Go to **Monitoring**. View the **Overall queue stats** charts.

{% endlist %}

### View the function logs {#function-logs}

The trigger should invoke the converter function for each message in the queue. To make sure the function is invoked, check its logs.

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder containing the `ffmpeg-converter` function.
  1. Select **Cloud Functions**.
  1. Select `ffmpeg-converter`.
  1. Go to the **Logs** tab and specify the period to view the logs for.

{% endlist %}

### Get a link to a GIF file {#get-link}

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), select the folder containing the `ffmpeg-api` function.
  1. Select **Cloud Functions**.
  1. Select the `ffmpeg-api` function.
  1. Navigate to the **Testing** tab.
  1. In the **Payload** field, enter the following request:

     ```json
     {"action":"get_task_status", "task_id":"<job_ID>"}
     ```

  1. Click **Run test**.
  1. If video conversion to GIF has not been completed, the **Function output** field will return:

     ```json
     {
         "ready": false
     }
     ```

     Otherwise, you will get a link to the GIF file:

     ```json
     {
         "ready": true,
         "gif_url": "https://storage.yandexcloud.net/<bucket_name>/1b4db1a6-f2b2-4b1c-b662-37f7********.gif?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=qxLftbbZ91U695ysemyZ%2F202********ru-central1%2Fs3%2Faws4_request&X-Amz-Date=20210831T110351Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=f4a5fe7848274a09be5b221fbf8a9f6f2b385708cfa351861a4e69df********"
     }
     ```

{% endlist %}


## How to delete the resources you created {#clear-out}

To stop paying for the resources you created:

1. Open the `video-converting.tf` file and delete your infrastructure description from it.
1. Apply the changes:

    1. In the terminal, navigate to the configuration file directory.
    1. Make sure the configuration is correct using this command:
    
       ```bash
       terraform validate
       ```
    
       If the configuration is valid, you will get this message:
    
       ```bash
       Success! The configuration is valid.
       ```
    
    1. Run this command:
    
       ```bash
       terraform plan
       ```
    
       You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
    1. Apply the configuration changes:
    
       ```bash
       terraform apply
       ```
    
    1. Type `yes` and press **Enter** to confirm the changes.

#### Useful links {#see-also}

* [Converting a video to a GIF in Python using the management console](console.md)