A startup script is a file that performs tasks during the startup process of a
virtual machine (VM) instance. Startup scripts can apply to all VMs in a project
or to a single VM. Startup scripts specified by VM-level metadata override
startup scripts specified by project-level metadata, and startup scripts only
run when a network is available. This document describes how to use startup
scripts on Windows Server VM instances. For information about how to add a
project-level startup script, see [`gcloud compute project-info add-metadata`](https://docs.cloud.google.com/sdk/gcloud/reference/compute/project-info/add-metadata).

Windows startup scripts must be Command shell (`.cmd`), PowerShell (`.ps1`), or
batch file scripts (`.bat`), and must have the appropriate file extension.

If you specify a startup script by using one of the procedures in this document,
Compute Engine does the following:

1. Copies the startup script to the VM

2. Task Scheduler runs the startup script as the [`LocalSystem`
   account](https://docs.microsoft.com/windows/win32/services/localsystem-account)
   when the VM boots

For information about the various tasks related to startup scripts and when to
perform each one, see the
[startup scripts overview document](https://docs.cloud.google.com/compute/docs/instances/startup-scripts).

## Prerequisites

To run scripts stored in metadata on a VM instance, the [guest environment](https://docs.cloud.google.com/compute/docs/images/guest-environment) must be installed and running.

- The guest environment includes the [guest agent](https://docs.cloud.google.com/compute/docs/images/guest-agent) (for example, google-guest-agent on Linux) that reads the script content or URL from the instance's metadata and initiates execution.
- All public Compute Engine images come with the guest environment preinstalled.
- If you create a custom image, you must [manually install the Google Guest Environment](https://docs.cloud.google.com/compute/docs/images/install-guest-environment) to ensure scripts from metadata and other Google Cloud features function properly.

## Before you begin

- Read the [overview of startup scripts](https://docs.cloud.google.com/compute/docs/instances/startup-scripts).
- Read about the basics of [VM metadata](https://docs.cloud.google.com/compute/docs/metadata/overview).
- If you haven't already, set up [authentication](https://docs.cloud.google.com/compute/docs/authentication). Authentication verifies your identity for access to Google Cloud services and APIs. To run code or samples from a local development environment, you can authenticate to Compute Engine by selecting one of the following options:

  Select the tab for how you plan to use the samples on this page:

  ### Console


  When you use the Google Cloud console to access Google Cloud services and
  APIs, you don't need to set up authentication.

  ### gcloud

  1.
     [Install](https://docs.cloud.google.com/sdk/docs/install) the Google Cloud CLI.

     After installation,
     [initialize](https://docs.cloud.google.com/sdk/docs/initializing) the Google Cloud CLI by running the following command:

     ```bash
     gcloud init
     ```


     If you're using an external identity provider (IdP), you must first
     [sign in to the gcloud CLI with your federated identity](https://docs.cloud.google.com/iam/docs/workforce-log-in-gcloud).

     > [!NOTE]
     > **Note:** If you installed the gcloud CLI previously, make sure you have the latest version by running `gcloud components update`.

  2. [Set a default region and zone](https://docs.cloud.google.com/compute/docs/gcloud-compute#set_default_zone_and_region_in_your_local_client).

  ### REST


  To use the REST API samples on this page in a local development environment, you use the
  credentials you provide to the gcloud CLI.
  1. [Install](https://docs.cloud.google.com/sdk/docs/install) the Google Cloud CLI.
  2. If you're using an external identity provider (IdP), you must first [sign in to the gcloud CLI with your federated identity](https://docs.cloud.google.com/iam/docs/workforce-log-in-gcloud).


  For more information, see
  [Authenticate for using REST](https://docs.cloud.google.com/docs/authentication/rest)
  in the Google Cloud authentication documentation.

## Metadata keys for Windows startup scripts

A startup script is passed to a VM from a location that is specified by a
metadata key. A metadata key specifies whether the startup script is stored
locally, stored in Cloud Storage, or passed directly to the VM. The
metadata key that you use might also depend on the size or the file type of the
startup script.

The following table shows the metadata keys that you can use for Windows startup
scripts, and provides information about which key to use based on the storage
location, size, and file type of the startup script.

| Metadata key | Use for |
|---|---|
| `sysprep-specialize-script-ps1` | Passing an unsigned PowerShell script that is stored locally or added directly and that is up to 256 KB in size |
| `sysprep-specialize-script-cmd` | Passing a command shell script that is stored locally or added directly and that is up to 256 KB in size |
| `sysprep-specialize-script-bat` | Passing a batch file script that is stored locally or added directly and that is up to 256 KB in size |
| `sysprep-specialize-script-url` | Passing a batch file, Command shell, signed/unsigned PowerShell script or executable that is stored in Cloud Storage and that is greater than 256 KB in size |
| `windows-startup-script-ps1` | Passing an unsigned PowerShell script that is stored locally or added directly and that is up to 256 KB in size |
| `windows-startup-script-cmd` | Passing a command shell script that is stored locally or added directly and that is up to 256 KB in size |
| `windows-startup-script-bat` | Passing a batch file script that is stored locally or added directly and that is up to 256 KB in size |
| `windows-startup-script-url` | Passing a batch file, Command shell, signed/unsigned PowerShell script or executable that is stored in Cloud Storage and that is greater than 256 KB in size |

For more information about the order of execution of the various types of
startup scripts, see the
[GoogleCloudPlatform/compute-image-windows](https://github.com/GoogleCloudPlatform/compute-image-windows/blob/5c2cf04a844e119043233a57d9af08e518d6e50b/metadata_scripts/GCEMetadataScripts/main.go#L349)
repository on GitHub.

## Order of execution of Windows startup scripts

You can use multiple startup scripts. Startup scripts stored locally or added
directly execute before startup scripts that are stored in
Cloud Storage. The type of file containing the script also impacts the
order of execution. The following table shows, based on the metadata key, the
order of execution of Windows startup scripts.

| Metadata key | Order of execution |
|---|---|
| `sysprep-specialize-script-ps1` | First during the initial boot |
| `sysprep-specialize-script-cmd` | Second during the initial boot |
| `sysprep-specialize-script-bat` | Third during the initial boot |
| `sysprep-specialize-script-url` | Fourth during the initial boot |
| `windows-startup-script-ps1` | First during each boot after the initial boot |
| `windows-startup-script-cmd` | Second during each boot after the initial boot |
| `windows-startup-script-bat` | Third during each boot after the initial boot |
| `windows-startup-script-url` | Fourth during each boot after the initial boot |

## Passing a Windows startup script directly

#### Permissions required for this task

To perform this task, you must have the following
[permissions](https://docs.cloud.google.com/iam/docs/overview#permissions):


- All permissions required to [create a new VM](https://docs.cloud.google.com/compute/docs/instances/create-start-instance).
- `compute.instances.setMetadata` on the VM.

Pass the contents of a batch file, Command shell, or unsigned PowerShell startup
script directly to a Windows Server VM. The following procedures show how to
pass an unsigned PowerShell script.

### Console

**Passing a Windows startup script directly to a new VM**

1. In the Google Cloud console, go to the **Create an instance** page.

   [Go to Create an instance](https://console.cloud.google.com/compute/instancesAdd)
2. For **Boot disk** , select **Change**, and do the following:

   1. On the **Public images** tab, choose a Windows Server operating system.
   2. Click **Select**.
3. Expand the **Advanced options** section, and do the
   following:

   1. Expand the **Management** section.
   2. In the **Metadata** section, click **Add item** to set the **Key**
      and **Value**:

      - **Key** : set to `windows-startup-script-ps1`.

        Startup scripts passed to the VM by using metadata keys beginning
        with `windows-startup-script` run on every boot after the VM's
        initial boot.
      - **Value**: add the following script:

            # Installing IIS
            Import-Module servermanager
            Install-WindowsFeature Web-Server -IncludeAllSubFeature

            # Ensure the directory exists
            if (-not (Test-Path("C:\inetpub\wwwroot"))) {New-Item "C:\inetpub\wwwroot" -Type Directory}

            # Write the expanded string out to the file, overwriting the file if it already exists.
            "<html><body><p>Windows startup script added directly.</p></body></html>" | Out-File -FilePath C:\inetpub\wwwroot\index.html -Encoding ascii -Force

4. Click **Create**.

**Passing a Windows startup script directly to an existing VM**

1. In the Google Cloud console, go to the **VM instances** page.

   [Go to VM instances](https://console.cloud.google.com/compute/instances)
2. Click the **Name** of the VM.

3. Click **Edit**.

4. Under **Metadata**, specify the following:

   - `key`: `windows-startup-script-ps1`
   - `value`: the contents of the startup script

**Verifying the startup script**

After the VM starts, view the external IP in a web browser to verify that
the startup script created the website. You might have to wait about 10
minutes for the sample startup script to finish.

### gcloud (Bash)


**Passing a Windows startup script directly to a new VM**


Pass the contents of a startup script directly to a Windows Server VM when you create it by
using the following [`gcloud compute
instances create` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create):

```
gcloud compute instances create VM_NAME \
  --image-project=windows-cloud \
  --image-family=windows-2019-core \
  --metadata=windows-startup-script-ps1='Import-Module servermanager
  Install-WindowsFeature Web-Server -IncludeAllSubFeature
  "<html><body><p>Windows startup script added directly.</p></body></html>" > C:\inetpub\wwwroot\index.html'
```


Replace <var translate="no">VM_NAME</var> with the name of the VM.
**Passing a Windows startup script directly to an existing VM**


Add the startup script directly to an existing VM by using the following
[`gcloud compute instances add-metadata` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME \
  --image-project=windows-cloud \
  --image-family=windows-2019-core \
  --metadata=windows-startup-script-ps1='Import-Module servermanager
  Install-WindowsFeature Web-Server -IncludeAllSubFeature
  "<html><body><p>Windows startup script added directly.</p></body></html>" > C:\inetpub\wwwroot\index.html'
```


Replace <var translate="no">VM_NAME</var> with the name of the VM.
**Verifying the startup script**


After the VM starts, view the external IP in a web browser to verify that the startup script
created the web site. You might have to wait about 10 minutes for the sample startup script to
finish.

<br />

### gcloud (Command Prompt)


**Passing a Windows startup script directly to a new VM**


Pass the contents of a startup script directly to a Windows Server VM when you create it by
using the following [`gcloud compute
instances create` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create):

```
gcloud compute instances create VM_NAME ^
  --image-project=windows-cloud ^
  --image-family=windows-2019-core ^
  --metadata=windows-startup-script-ps1='Import-Module servermanager
  Install-WindowsFeature Web-Server -IncludeAllSubFeature
  "<html><body><p>Windows startup script added directly.</p></body></html>" > C:\inetpub\wwwroot\index.html'
```


Replace <var translate="no">VM_NAME</var> with the name of the VM.
**Passing a Windows startup script directly to an existing VM**


Add the startup script directly to an existing VM by using the following
[`gcloud compute instances add-metadata` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME ^
  --image-project=windows-cloud ^
  --image-family=windows-2019-core ^
  --metadata=windows-startup-script-ps1='Import-Module servermanager
  Install-WindowsFeature Web-Server -IncludeAllSubFeature
  "<html><body><p>Windows startup script added directly.</p></body></html>" > C:\inetpub\wwwroot\index.html'
```


Replace <var translate="no">VM_NAME</var> with the name of the VM.
**Verifying the startup script**


After the VM starts, view the external IP in a web browser to verify that the startup script
created the web site. You might have to wait about 10 minutes for the sample startup script to
finish.

<br />

### gcloud (PowerShell)


**Passing a Windows startup script directly to a new VM**


Pass the contents of a startup script directly to a Windows Server VM when you create it by
using the following [`gcloud compute
instances create` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create):

```
gcloud compute instances create VM_NAME `
  --image-project=windows-cloud `
  --image-family=windows-2019-core `
  --metadata=windows-startup-script-ps1='Import-Module servermanager
  Install-WindowsFeature Web-Server -IncludeAllSubFeature
  "<html><body><p>Windows startup script added directly.</p></body></html>" > C:\inetpub\wwwroot\index.html'
```


Replace <var translate="no">VM_NAME</var> with the name of the VM.
**Passing a Windows startup script directly to an existing VM**


Add the startup script directly to an existing VM by using the following
[`gcloud compute instances add-metadata` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME `
  --image-project=windows-cloud `
  --image-family=windows-2019-core `
  --metadata=windows-startup-script-ps1='Import-Module servermanager
  Install-WindowsFeature Web-Server -IncludeAllSubFeature
  "<html><body><p>Windows startup script added directly.</p></body></html>" > C:\inetpub\wwwroot\index.html'
```


Replace <var translate="no">VM_NAME</var> with the name of the VM.
**Verifying the startup script**


After the VM starts, view the external IP in a web browser to verify that the startup script
created the web site. You might have to wait about 10 minutes for the sample startup script to
finish.

<br />

### REST

**Passing a Windows startup script directly to a new VM**

Pass the contents of a startup script directly to a Windows Server VM
when you create it by using the following
[`instances.insert` method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/insert).

```
POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances

{
  ...
  "networkInterfaces": [
    {
      "accessConfigs": [
        {
          "type": "ONE_TO_ONE_NAT"
        }
      ]
    }
  ],
  "metadata": {
    "items": [
      {
        "key": "windows-startup-script-ps1",
        "value": "Import-Module servermanager\nInstall-WindowsFeature Web-Server -IncludeAllSubFeature\necho '<html><body><p>Windows startup script added directly.</p></body></html>' > C:\\inetpub\\wwwroot\\index.html"
      }
    ]
  },
  ...
}
```

Replace the following:

- <var translate="no">PROJECT_ID</var>: the project ID.

- <var translate="no">ZONE</var>: the zone to create the VM in.

**Passing a Windows startup script directly to an existing VM**

1. Get the `metadata.fingerprint` value of the VM by using the
   [`instances.get` method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/get):

   ```
   GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME
   ```

   Replace the following:
   - <var translate="no">PROJECT_ID</var>: the ID of the project where the VM
     exists.

   - <var translate="no">ZONE</var>: the zone of the VM.

   - <var translate="no">VM_NAME</var>: the name of the VM.

2. Pass the startup script by using the `fingerprint` value, along with the
   metadata key and value for the startup script, in a call to the
   [`instances.setMetadata` method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/setMetadata):

   ```
   POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/setMetadata

   {
    "fingerprint": FINGERPRINT,
    "items": [
      {
        "key": "windows-startup-script-ps1",
        "value": "Import-Module servermanager\nInstall-WindowsFeature Web-Server -IncludeAllSubFeature\necho '<html><body><p>Windows startup script added directly.</p></body></html>' > C:\\inetpub\\wwwroot\\index.html"
      }
    ],
    ...
   }
   ```

   Replace the following:
   - <var translate="no">PROJECT_ID</var>: the ID of the project where the VM
     exists.

   - <var translate="no">ZONE</var>: the zone of the VM.

   - <var translate="no">VM_NAME</var>: the name of the VM.

   - <var translate="no">FINGERPRINT</var>: the `metadata.fingerprint` value
     obtained by using the [`instances.get`
     method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/get).

**Verifying the startup script**

After the VM starts, view the external IP in a web browser to verify that
the startup script created the website. You might have to wait about 10
minutes for the sample startup script to finish.

## Passing a Windows startup script from a local file

#### Permissions required for this task

To perform this task, you must have the following
[permissions](https://docs.cloud.google.com/iam/docs/overview#permissions):


- All permissions required to [create a new VM](https://docs.cloud.google.com/compute/docs/instances/create-start-instance).
- `compute.instances.setMetadata` on the VM.

You can store a startup script in a local batch file, Command shell script, or
unsigned PowerShell script on your workstation and pass the local file as
metadata to a VM when you create it. You cannot use files stored on VMs as
startup scripts.

Before passing a Windows startup script from a local file to a VM, do the
following:

1. Create a local (`.ps1`) file to store the startup script.

2. Note the relative path from gcloud CLI to the startup script.

3. Add the following startup script to the file:

       # Installing IIS
       Import-Module servermanager
       Install-WindowsFeature Web-Server -IncludeAllSubFeature

       # Ensure the directory exists
       if (-not (Test-Path("C:\inetpub\wwwroot"))) {New-Item "C:\inetpub\wwwroot" -Type Directory}

       # Write the expanded string out to the file, overwriting the file if it already exists.
       "<html><body><p>Windows startup script passed from a file on your local workstation.</p></body></html>" | Out-File -FilePath C:\inetpub\wwwroot\index.html -Encoding ascii -Force

### gcloud (Bash)


**Passing a Windows startup script from a local file to a new VM**


Create a VM and pass the contents of a local file to be used as the startup script by using the
[`gcloud compute instances create`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create) with the `--metadata-from-file` flag:

```
gcloud compute instances create VM_NAME \
  --image-project=windows-cloud \
  --image-family=windows-2019-core \
  --metadata-from-file=windows-startup-script-ps1=FILE_PATH
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM
- <var translate="no">FILE_PATH</var>: the relative path to the startup script file

**Passing a Windows startup script from a local file to an existing VM**


Pass a startup script to an existing VM from a local file by using the following
[`gcloud compute instances add-metadata` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME \
  --metadata-from-file=windows-startup-script-ps1=FILE_PATH
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM
- <var translate="no">FILE_PATH</var>: the relative path to the startup script file

**Verifying the startup script**


View the external IP in a web browser to verify that the startup script
created the web site. You might have to wait about 10 minutes for the sample
startup script to finish.

<br />

### gcloud (Command Prompt)


**Passing a Windows startup script from a local file to a new VM**


Create a VM and pass the contents of a local file to be used as the startup script by using the
[`gcloud compute instances create`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create) with the `--metadata-from-file` flag:

```
gcloud compute instances create VM_NAME ^
  --image-project=windows-cloud ^
  --image-family=windows-2019-core ^
  --metadata-from-file=windows-startup-script-ps1=FILE_PATH
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM
- <var translate="no">FILE_PATH</var>: the relative path to the startup script file

**Passing a Windows startup script from a local file to an existing VM**


Pass a startup script to an existing VM from a local file by using the following
[`gcloud compute instances add-metadata` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME ^
  --metadata-from-file=windows-startup-script-ps1=FILE_PATH
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM
- <var translate="no">FILE_PATH</var>: the relative path to the startup script file

**Verifying the startup script**


View the external IP in a web browser to verify that the startup script
created the web site. You might have to wait about 10 minutes for the sample
startup script to finish.

<br />

### gcloud (PowerShell)


**Passing a Windows startup script from a local file to a new VM**


Create a VM and pass the contents of a local file to be used as the startup script by using the
[`gcloud compute instances create`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create) with the `--metadata-from-file` flag:

```
gcloud compute instances create VM_NAME `
  --image-project=windows-cloud `
  --image-family=windows-2019-core `
  --metadata-from-file=windows-startup-script-ps1=FILE_PATH
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM
- <var translate="no">FILE_PATH</var>: the relative path to the startup script file

**Passing a Windows startup script from a local file to an existing VM**


Pass a startup script to an existing VM from a local file by using the following
[`gcloud compute instances add-metadata` command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME `
  --metadata-from-file=windows-startup-script-ps1=FILE_PATH
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM
- <var translate="no">FILE_PATH</var>: the relative path to the startup script file

**Verifying the startup script**


View the external IP in a web browser to verify that the startup script
created the web site. You might have to wait about 10 minutes for the sample
startup script to finish.

<br />

## Passing a Windows startup script from Cloud Storage

#### Permissions required for this task

To perform this task, you must have the following
[permissions](https://docs.cloud.google.com/iam/docs/overview#permissions):


- All permissions required to [create a new VM](https://docs.cloud.google.com/compute/docs/instances/create-start-instance).
- `compute.instances.setMetadata` on the VM.
- Permission to access the bucket and script file in Cloud Storage. Check the [access control](https://docs.cloud.google.com/storage/docs/access-control) settings on the bucket and file to ensure you have permission.

You can store a startup script as a batch file, Command shell script,
signed/unsigned PowerShell script or executable in Cloud Storage and
pass it to a VM when you create it. After you add a startup script to
Cloud Storage, you have a URL that you can use to reference the startup
script when you create a VM.

Before adding a startup script from a Cloud Storage bucket, do the
following:

1. Create a file to store the startup script. This example uses a PowerShell
   (`.ps1`) file.

2. Add the following PowerShell script to the file, which installs a web server
   and creates a simple web page:

       # Installing IIS
       Import-Module servermanager
       Install-WindowsFeature Web-Server -IncludeAllSubFeature

       # Ensure the directory exists
       if (-not (Test-Path("C:\inetpub\wwwroot"))) {New-Item "C:\inetpub\wwwroot" -Type Directory}

       # Write the expanded string out to the file, overwriting the file if it already exists.
       "<html><body><p>Windows startup script passed from Cloud Storage.</p></body></html>" | Out-File -FilePath C:\inetpub\wwwroot\index.html -Encoding ascii -Force

3. [Create a Cloud Storage bucket](https://docs.cloud.google.com/storage/docs/creating-buckets).

4. [Add the file to the Cloud Storage bucket](https://docs.cloud.google.com/storage/docs/uploading-objects).

### Security implications

- By default, project owners and project editors can access
  Cloud Storage files in the same project, unless there are explicit
  access controls that disallow it.

- If the Cloud Storage bucket or object is less secure than metadata,
  there is a risk of privilege escalation if the startup script is modified and
  the VM reboots. This is because after the VM reboots, the startup script runs
  as `LocalSystem` and can then use the permissions of the attached service
  account to access other resources.

### Console

**Passing a startup script that is stored in Cloud Storage to a new VM**

1. In the Google Cloud console, go to the **Create an instance** page.

   [Go to Create an instance](https://console.cloud.google.com/compute/instancesAdd)
2. For **Boot disk** , select **Change**, and do the following:

   1. On the **Public images** tab, choose a Windows Server operating system.
   2. Click **Select**.
3. In the **Identity and API access** section, select a service account that
   has the [Storage Object Viewer](https://docs.cloud.google.com/storage/docs/access-control/iam-roles)
   role.

4. Expand the **Advanced options** section, and then do the
   following:

   1. Expand the **Management** section.
   2. In the **Metadata** section, add values for the following:

      - **Key** : the metadata key. Specify the `windows-startup-script-url`
        metadata key so the script runs during every boot after the initial
        boot.

      - **Value**: the metadata value. Set to the Cloud Storage
        location of the startup script file using one of the following formats:

        - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
        - **gcloud storage URI** : `gs://BUCKET/FILE`

        Replace the following:
        - <var translate="no">BUCKET</var>: the name of the bucket that contains the startup script file
        - <var translate="no">FILE</var>: the name of the startup script file
5. To create the VM, click **Create**.

**Passing a startup script that is stored in Cloud Storage to an existing VM**

1. In the Google Cloud console, go to the **VM instances** page.

   [Go to VM instances](https://console.cloud.google.com/compute/instances)
2. Click the **Name** of the VM.

3. Click **Edit**.

4. Under **Metadata**, add the following values:

   - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
   - **gcloud storage URI** : `gs://BUCKET/FILE`

**Verifying the startup script**

View the external IP in a web browser to verify that the startup script
created the website. You might have to wait about 10 minutes for the sample
startup script to finish.

### gcloud (Bash)


**Passing a startup script that is stored in Cloud Storage to a new VM**


Pass a startup script that is stored in Cloud Storage to a Windows
Server VM by using the following [`gcloud compute instances create`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create). For the value of the `--scopes` flag, use `storage-ro` so the VM can access
Cloud Storage.

```
gcloud compute instances create VM_NAME \
  --image-project=windows-cloud \
  --image-family=windows-2019-core \
  --scopes=storage-ro \
  --metadata=windows-startup-script-url=CLOUD_STORAGE_URL
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM.
- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata values. Set to the location of the startup script file by using one of the following formats:
  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Passing a startup script that is stored in Cloud Storage to an existing VM**


Pass a startup script that is stored in Cloud Storage to an
existing VM by using the following [`gcloud compute instances add-metadata`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME \
  --metadata=windows-startup-script-url=CLOUD_STORAGE_URL
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM.
- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata values. Set to the location of the startup script file by using one of the following formats:
  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Verifying the startup script**


View the external IP in a web browser to verify that the startup script created the web site. You
might have to wait about 10 minutes for the sample startup script to finish.

<br />

### gcloud (Command Prompt)


**Passing a startup script that is stored in Cloud Storage to a new VM**


Pass a startup script that is stored in Cloud Storage to a Windows
Server VM by using the following [`gcloud compute instances create`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create). For the value of the `--scopes` flag, use `storage-ro` so the VM can access
Cloud Storage.

```
gcloud compute instances create VM_NAME ^
  --image-project=windows-cloud ^
  --image-family=windows-2019-core ^
  --scopes=storage-ro ^
  --metadata=windows-startup-script-url=CLOUD_STORAGE_URL
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM.
- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata values. Set to the location of the startup script file by using one of the following formats:
  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Passing a startup script that is stored in Cloud Storage to an existing VM**


Pass a startup script that is stored in Cloud Storage to an
existing VM by using the following [`gcloud compute instances add-metadata`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME ^
  --metadata=windows-startup-script-url=CLOUD_STORAGE_URL
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM.
- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata values. Set to the location of the startup script file by using one of the following formats:
  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Verifying the startup script**


View the external IP in a web browser to verify that the startup script created the web site. You
might have to wait about 10 minutes for the sample startup script to finish.

<br />

### gcloud (PowerShell)


**Passing a startup script that is stored in Cloud Storage to a new VM**


Pass a startup script that is stored in Cloud Storage to a Windows
Server VM by using the following [`gcloud compute instances create`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create). For the value of the `--scopes` flag, use `storage-ro` so the VM can access
Cloud Storage.

```
gcloud compute instances create VM_NAME `
  --image-project=windows-cloud `
  --image-family=windows-2019-core `
  --scopes=storage-ro `
  --metadata=windows-startup-script-url=CLOUD_STORAGE_URL
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM.
- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata values. Set to the location of the startup script file by using one of the following formats:
  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Passing a startup script that is stored in Cloud Storage to an existing VM**


Pass a startup script that is stored in Cloud Storage to an
existing VM by using the following [`gcloud compute instances add-metadata`
command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/add-metadata):

```
gcloud compute instances add-metadata VM_NAME `
  --metadata=windows-startup-script-url=CLOUD_STORAGE_URL
```


Replace the following:

- <var translate="no">VM_NAME</var>: the name of the VM.
- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata values. Set to the location of the startup script file by using one of the following formats:
  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Verifying the startup script**


View the external IP in a web browser to verify that the startup script created the web site. You
might have to wait about 10 minutes for the sample startup script to finish.

<br />

### REST

**Passing a startup script that is stored in Cloud Storage to a new VM**

Pass a startup script that is stored in Cloud Storage to a Windows
Server VM by using the following [`instances.insert`
method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/insert). To
the `scopes` field, add
`https://www.googleapis.com/auth/devstorage.read_only` so the VM can access
Cloud Storage.

```
POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances

{
  ...
  "networkInterfaces": [
    {
      "accessConfigs": [
        {
          "type": "ONE_TO_ONE_NAT"
        }
      ]
    }
  ],
  "serviceAccounts": [
    {
      "email": "default",
      "scopes": [
        "https://www.googleapis.com/auth/devstorage.read_only"
      ]
    }
  ],
  "metadata": {
    "items": [
      {
        "key": "windows-startup-script-url",
        "value": "CLOUD_STORAGE_URL"
      },
      ...
    ]
  },
  ...
}
```

Replace the following:

- <var translate="no">PROJECT_ID</var>: the project ID.

- <var translate="no">ZONE</var>: the zone to create the VM in.

- <var translate="no">CLOUD_STORAGE_URL</var>: the metadata value. Set to
  the Cloud Storage location of the startup script file using one
  of the following formats:

  - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
  - **gcloud storage URI** : `gs://BUCKET/FILE`

**Passing a startup script that is stored in Cloud Storage to an existing VM**

1. Get the `metadata.fingerprint` value of the VM by using the
   [`instances.get` method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/get):

   ```
   GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME
   ```

   Replace the following:
   - <var translate="no">PROJECT_ID</var>: the ID of the project where the VM
     exists.

   - <var translate="no">ZONE</var>: the zone of the VM.

   - <var translate="no">VM_NAME</var>: the name of the VM

2. Pass the startup script by using the `fingerprint` value, along with the
   metadata key and value for the startup script, in a call to the
   [`instances.setMetadata` method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/setMetadata):

   ```
   POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/setMetadata

   {
     "fingerprint": FINGERPRINT,
     "items": [
       {
           "key": "windows-startup-script-url",
           "value": "CLOUD_STORAGE_URL"
       }
     ],
     ...
   }
   ```

   Replace the following:
   - <var translate="no">PROJECT_ID</var>: the ID of the project where the VM
     exists.

   - <var translate="no">ZONE</var>: the zone of the VM.

   - <var translate="no">VM_NAME</var>: the name of the VM.

   - <var translate="no">FINGERPRINT</var>: the `metadata.fingerprint` value
     obtained by using the [`instances.get`
     method](https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/get).

   - <var translate="no">CLOUD_STORAGE_URL</var>: the metadata value. Set to
     the Cloud Storage location of the startup script file using one
     of the following formats:

     - **Authenticated URL** : `https://storage.googleapis.com/BUCKET/FILE`
     - **gcloud storage URI** : `gs://BUCKET/FILE`

**Verifying the startup script**

View the external IP in a web browser to verify that the startup script
created the website. You might have to wait about 10 minutes for the sample
startup script to finish.

## Accessing metadata from a Windows startup script

In a startup script you can access metadata values. For example, you can use the
same script for multiple VMs, and parameterize each script individually by
passing different metadata values to each VM.

To access a custom metadata value from a startup script, do the following:

1. Create a startup script that queries the value of a metadata key. For
   example, the following PowerShell (`.ps1`) startup script queries the value of the
   `foo` metadata key.

       $METADATA_VALUE = (Invoke-RestMethod -Headers @{'Metadata-Flavor' = 'Google'} -Uri "http://metadata.google.internal/computeMetadata/v1/instance/attributes/foo")

       # Installing IIS
       Import-Module servermanager
       Install-WindowsFeature Web-Server -IncludeAllSubFeature

       # Ensure the directory exists
       if (-not (Test-Path("C:\inetpub\wwwroot"))) {New-Item "C:\inetpub\wwwroot" -Type Directory}

       # Write the expanded string out to the file, overwriting the file if it already exists.
       "<html><body><p>Accessing metadata value of foo: $METADATA_VALUE</p></body></html>" | Out-File -FilePath C:\inetpub\wwwroot\index.html -Encoding ascii -Force

2. Set the value of the `foo` metadata key when creating a VM by using the
   following [`gcloud compute instances create`
   command](https://docs.cloud.google.com/sdk/gcloud/reference/compute/instances/create). For this example,
   the startup script is passed to the VM from a local file.

   ### gcloud

   ```
   gcloud compute instances create VM_NAME \
     --image-project=windows-cloud \
     --image-family=windows-2019 \
     --metadata-from-file=windows-startup-script-ps1=FILE_PATH \
     --metadata=foo=bar
   ```

   Replace the following:
   - <var translate="no">VM_NAME</var>: the name of the VM

   - <var translate="no">FILE_PATH</var>: the relative path to the startup
     script file

   For more information about how to specify a metadata key-value pair, see
   [Set and remove custom metadata](https://docs.cloud.google.com/compute/docs/metadata/setting-custom-metadata).
3. View the external IP in a web browser to verify that the startup script
   outputs the value of `foo`. You might have to wait about 10 minutes for the
   sample startup script to finish.

## Rerunning a Windows startup script

Rerun a startup script on a Windows VM by doing the following:

1. [Connecting to the VM](https://docs.cloud.google.com/compute/docs/instances/connecting-to-windows).

2. Running the following command:

   ```
   C:\Program Files\Google\Compute Engine\metadata_scripts\run_startup_scripts.cmd
   ```

## Viewing the output from a Windows startup script

View the output from a Windows Server startup script by using any of the
following and checking for `GCEMetadataScripts` events:

- Serial port 1 in the Google Cloud console. For more information, see
  [Viewing serial port output](https://docs.cloud.google.com/compute/docs/instances/viewing-serial-port-output#viewing_serial_port_output).

- Windows Event Viewer's Application Log.

- IAP Desktop from a Windows workstation. For more information, see the
  [GoogleCloudPlatform/iap-desktop](https://github.com/GoogleCloudPlatform/iap-desktop)
  repository on GitHub.

## What's next

- Learn how to [use startup scripts on Linux VMs](https://docs.cloud.google.com/compute/docs/instances/startup-scripts/linux).

- Learn how to [add a shutdown script](https://docs.cloud.google.com/compute/docs/shutdownscript).

- Learn more about [VM metadata](https://docs.cloud.google.com/compute/docs/metadata/overview).

- Learn how to [run startup scripts on Windows VMs and join a
  Managed Microsoft AD domain](https://docs.cloud.google.com/managed-microsoft-ad/docs/seamless-domain-join-gce).