The Instructure Community will enter a read-only state on November 22, 2025 as we prepare to migrate to our new Community platform in early December.
Read our blog post for more info about this change.
Our campuses want to copy a couurse template to the all the course shells created in the SIS import. I thought I can use the Powershell script available in the unsupported GitHub Canvas site. When I try to run the script, I get the message:
Starting copy of course .......
ERROR: An error occured: System.Net.WebException: The remote server returned an error: (404) Not Found.
at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
I want to know if anybody out there has tried this and got it to work. I am trying to run the script in a windows 7 machine.
The PS version is 3.0.
This is the script if anybody wants to look at it.
-----------------------------------------------------------------------------------------------------------------------------------------------------
# Script to copy course content from one existing course to another
# Version 0.1
#Script Requires PowerShell 3 or higher
# Input File Format is a CSV file with the following headers:
# source_id,destination_id
# Configuration Variables
$canvasURL = 'rsccd.test.instructure.com' # Canvas Instance URL do not include https:// (Example: canvas.instructure.com)
$token = '' # An API token for a user with adequate permission to assign observees -- I placed our access token within ''
$inputFile = 'c:\canvas\copycourses.csv' # Full Path to file to process
$useSISIDs = $false # Set to $true if your input file provides SIS IDs for both source and destination course IDs. Set to $false if your input file uses Canvas IDs for both source and destination course IDs.
#************ Do Not Edit Below This Line ************
#Check for file
if(!(Test-Path $inputFile)){
Write-Host Input file does not exist!
exit
}
$headers = @{"Authorization"="Bearer "+$token}
$in_data = Import-CSV($inputFile);
if($useSISIDs){
$idPrefix = "sis_course_id:"
}else{
$idPrefix = ""
}
forEach($item in $in_data){
$fromCourse = $idPrefix + $item.source_id
$toCourse = $idPrefix + $item.destination_id
$url = 'https://'+$canvasURL+'/api/v1/courses/'+$toCourse+'/content_migrations'
#Write-Host $url
$postData = @{'migration_type'='course_copy_importer'; 'settings[source_course_id]'=$fromCourse}
Write-Host Starting copy of course $fromCourse to course $toCourse
Try {
$results = (Invoke-WebRequest -Headers $headers -Method POST -Uri $url -Body $postData)
Try{
$jresults = ($results.Content | ConvertFrom-Json)
if($jresults.id){
Write-Host " Started"
}
}Catch{
Write-Host " Unable to complete request. An error occured: " + $_.Exception
}
} Catch {
$errorMsg = $_.Exception
#$errorMsg
$terminate = $false
if($errorMsg.Response.StatusCode -eq 'unauthorized'){
$msg = "The provided token is not from a user with sufficient permission to complete the action."
$terminate = $true
}elseif($errorMsg.Response.StatusCode.Value__ -eq "400"){
$msg = "Unable to start copy."
$terminate = $false
}else{
$msg = "An error occured: " + $errorMsg
$terminate = $true
}
Write-Host " ERROR:" $msg
if($terminate){ break }
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------
Thank you,
Asha Vanch
Rancho Santiago Community College District
Solved! Go to Solution.
So that means your CSV file contains the SIS_ID for a course in the destination id column. Could you verify that you are also using the SIS_ID in the source id column? My guess is that one column is SIS ID and the other is Canvas Course ID.
@vanch_asha , I've shared your question with the Canvas Developers group to get their troubleshooting help. And in case you're not locked into the idea of using a Powershell script, have a look at , which describes how to use Postman to achieve the desired result.
Since you are getting a 404 error, I imagine that the URL is incorrect. Are you sending the SIS course ID or the Canvas course ID? I would bet it has something to do with that. It might be as simple as flipping that boolean $useSISIDs variable to true.
Try uncommenting that line right below where the $url variable is set which will write out the url to the console. See if the URL looks right.
Matt,
Yes, I agree with you the URL does not seem not correct. I uncommented the Write-Host #url and this is where it points to:
https://rsccd.test.instructure.com/api/v1/accounts/1/courses/sis_course_id:SAC-Sandbox-Abraham-Ricke...migrations
I am not sure where it should point to in order to copy the template into the course shell.
I tried flipping the boolean $useSISIDs to a Yes and it gives me the following error now: The remote server returned an error: (422) Unprocessable Entity.
I am not sure what needs to be changed.
Thank you for your reply.
Asha
For clarification, is that URL before or after you swapped $useSISIDs?
It was before and after I swapped $useSISIDs. The error changed when I swapped the $useSISIDs to 422 - Unprocessable Error.
Thank you,
Asha
It was before and after I swapped $useSISIDs. The error changed when I swapped the $useSISIDs to 422 - Unprocessable Entity (not Error).
Thank you,
Asha
So that means your CSV file contains the SIS_ID for a course in the destination id column. Could you verify that you are also using the SIS_ID in the source id column? My guess is that one column is SIS ID and the other is Canvas Course ID.
You are awesome. That was it. So I checked one course (destination) had a SIS ID in Canvas and other course (source) did not have a SIS ID in Canvas. Once I created the SIS ID for the course - source_id, the script worked. Now I have to figure out how to do it for several course shells.
Thank you Matt-you made my day:)
Glad I could help! I was going to try to run the code myself to see if I could reproduce the error but I don't really know Powershell at all. So I'm glad we were able to debug it using just the 404 and 422 error codes!
Thank you very much. I really appreciate your help.
Ash, will you repost your script with the correct information and the multiple course shell, too.
Thanks
John Boekennogen
University of Oklahoma
Here is the code that worked:
*****************************************************************************************************************************************************
Start of Powershell Script
*****************************************************************************************************************************************************
# Script to copy course content from one existing course to another
# Version 0.1
#Script Requires PowerShell 3 or higher
# Input File Format is a CSV file with the following headers:
# source_id,destination_id
# Configuration Variables
$canvasURL = 'rsccd.test.instructure.com' # Canvas Instance URL do not include https:// (Example: canvas.instructure.com)
(this line was edited to remove token)
$inputFile = 'c:\canvas\copycourses.csv' # Full Path to file to process
$useSISIDs = $true # Set to $true if your input file provides SIS IDs for both source and destination course IDs. Set to $false if your input file uses Canvas IDs for both source and destination course IDs.
#************ Do Not Edit Below This Line ************
#Check for file
if(!(Test-Path $inputFile)){
Write-Host Input file does not exist!
exit
}
$headers = @{"Authorization"="Bearer "+$token}
$in_data = Import-CSV($inputFile);
if($useSISIDs){
$idPrefix = "sis_course_id:"
}else{
$idPrefix = ""
}
forEach($item in $in_data){
$fromCourse = $idPrefix + $item.source_id
$toCourse = $idPrefix + $item.destination_id
$url = 'https://'+$canvasURL+'/api/v1/courses/'+$toCourse+'/content_migrations'
Write-Host $url
$postData = @{'migration_type'='course_copy_importer'; 'settings[source_course_id]'=$fromCourse}
Write-Host Starting copy of course $fromCourse to course $toCourse
Try {
$results = (Invoke-WebRequest -Headers $headers -Method POST -Uri $url -Body $postData)
Try{
$jresults = ($results.Content | ConvertFrom-Json)
if($jresults.id){
Write-Host " Started"
}
}Catch{
Write-Host " Unable to complete request. An error occured: " + $_.Exception
}
} Catch {
$errorMsg = $_.Exception
#$errorMsg
$terminate = $false
if($errorMsg.Response.StatusCode -eq 'unauthorized'){
$msg = "The provided token is not from a user with sufficient permission to complete the action."
$terminate = $true
}elseif($errorMsg.Response.StatusCode.Value__ -eq "400"){
$msg = "Unable to start copy."
$terminate = $false
}else{
$msg = "An error occured: " + $errorMsg
$terminate = $true
}
Write-Host " ERROR:" $msg
if($terminate){ break }
}
Write-Host Done
}
*****************************************************************************************************************************************************
End of Powershell Script
*****************************************************************************************************************************************************
I wanted to copy the course template from course named "TC Template 1" to course named "SAC Sandbox" (empty course shell imported via SIS import).
I noticed the "SAC Sandbox" had a SIS ID but the "TC Template 1" did not have a SIS ID filled out in Canvas. (You can see it when you go into the course and click on Settings )
The script did not work even when I changed $useSISIDs to false .
Once I added a SIS ID ( "TCTemp1" ) for the "TC Template 1" course- this is what the courses looked like in Canvas.
TC Template 1 with SIS ID TCTemp1
SAC Sandbox with SAC-Sandbox
For testing the script with one course, I had my copycourses.csv filled with only one course:
This is what the csv file looked like:
source_id, destination_id
TCTemp1, SAC-Sandbox
I changed the Powershell Script set $useSISIDs to true and it worked.
For multiple courses, the only change I had to do was create a csv file with all courses.
This is a sample:
here I have one template copied to several course shells created by SIS import.
source_id, destination_id
TCTemp1, SAC-Sandbox
TCTemp1, course1
TCTemp1, course2
TCTemp1, course3
TCTemp1, course4
TCTemp1, course5
TCTemp1, course6
TCTemp1, course7
TCTemp1, course8
TCTemp1, course9
TCTemp1, course10
(In this file the destination_id column is populated by are SIS IDs of course shells ).
Let me know if there are questions.
Thank you,
Asha
Community helpTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign inTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign in
This discussion post is outdated and has been archived. Please use the Community question forums and official documentation for the most current and accurate information.