I've been using Pulumi for a few months and have s...
# automation-api
b
I've been using Pulumi for a few months and have started to dip my toes into the Automation API. Everything I run this code, a new workspace is being created so a new stack is created every time also. What's going on here? (edited to show current code state)
Copy code
def main():
    """Setup the Pulumi application environment"""
    appName="simple-app"
    factoryId="CL1004"
    stackEnv="dev"
    region="us-east-1"
    vpcID="vpc-0e9250c80816d4a96"
    numAZs="3"
    dbType="docdb"
    if appName and factoryId:
        projName=factoryId+ "-" + appName
    else:
        return "Please provide both an application name and a factory ID."
    os.environ['PULUMI_CONFIG_PASSPHRASE'] = ''

    # Create working directory for the application stacks (environments)
    if not os.path.exists(projName):
        try:
            pathlib.Path(projName).mkdir(parents=True)
            work_dir=str(projName)
        except FileExistsError:
            "Application folder already exists."
            work_dir=str(projName)
    else:
        work_dir=str(projName)

    # Select or setup the stack
    try:
        stack = auto.create_or_select_stack(
         stack_name=stackEnv,
         project_name=projName,
         program=pulumi_program,
         work_dir=work_dir
        )
        stack.workspace.install_plugin("aws", "6.56.1")
        stack.set_config("aws:region", auto.ConfigValue(value=region))
        stack.set_config("aws:profile", auto.ConfigValue(value="<redacted>"))
        stack.set_config("vpcID", auto.ConfigValue(value=vpcID))
        stack.set_config("numAZs", auto.ConfigValue(value=numAZs))
        stack.set_config("dbType", auto.ConfigValue(value=dbType))
        print(f"Created new stack '{stackEnv}' for '{projName}'.")
    except auto.StackAlreadyExistsError:
        print(f"Stack '{stackEnv}' for '{projName}' already exists.")
    except auto.RuntimeError as e:
        print(f"Runtime error: {e}")
    except auto.RuntimeError as e:
        print(f"Runtime error: {e}")
s
have you looked at just using
.create_or_select_stack
or is there i a specific reason you want to handle selecting or creating the stack yourself?
b
I originally started using
.create_or_select_stack
but it appeared to be doing the same thing: always creating a new workspace and stack.
I just refactored the code to use
.create_or_select_stack
but it's doing the same thing, creating a new workspace and stack with every execution. For reference, this is how I'm setting up the environment:
Copy code
def main():
    """Setup the Pulumi application environment"""
    appName="simple-app"
    factoryId="CL1004"
    stackEnv="dev"
    region="us-east-1"
    vpcID="<redacted>"
    numAZs="3"
    dbType="docdb"
    if appName and factoryId:
        projName=factoryId+ "-" + appName
    else:
        return "Please provide both an application name and a factory ID."
    os.environ['PULUMI_CONFIG_PASSPHRASE'] = ''

# Create working directory for the application stacks
if not os.path.exists(projName):
        try:
            pathlib.Path(projName).mkdir(parents=True)
            work_dir=str(projName)
        except FileExistsError:
            "Application folder already exists."
            work_dir=str(projName)
    else:
        work_dir=str(projName)

    # Select or setup the stack
    try:
        stack = auto.create_or_select_stack(
         stack_name=stackEnv,
         project_name=projName,
         program=pulumi_program,
         work_dir=work_dir
        )
        stack.workspace.install_plugin("aws", "6.56.1")
        stack.set_config("aws:region", auto.ConfigValue(value=region))
        stack.set_config("aws:profile", auto.ConfigValue(value="<redacted>"))
        stack.set_config("vpcID", auto.ConfigValue(value=vpcID))
        stack.set_config("numAZs", auto.ConfigValue(value=numAZs))
        stack.set_config("dbType", auto.ConfigValue(value=dbType))
        print(f"Created new stack '{stackEnv}' for '{projName}'.")
    except auto.StackAlreadyExistsError:
        print(f"Stack '{stackEnv}' for '{projName}' already exists.")
    except auto.RuntimeError as e:
        print(f"Runtime error: {e}")
    except auto.RuntimeError as e:
        print(f"Runtime error: {e}")
s
i assume you've let it created the stack once?
b
I have.
s
are the resources changing each time?
b
They are not.
s
just the stack wants to recreate?
maybe try doing
stack_name_fqdn = pulumi.automation.fully_qualified_stack_name(pulumi_org, pulumi_project, stack_name)
and using the FQDN as the stack name
something might be getting confused
then use
stack = pulumi.automation.create_or_select_stack(stack_name=stack_name_fqdn, work_dir=work_dir)
with i think the program_name.. also in your case.. otherwise, its a strange problem. I've done it both ways.. in our environment.
Copy code
try:
        pulumi.automation.select_stack(stack_name=stack_name_fqdn, work_dir=work_dir)
        print("Found existing docker stack")
        stack_exists = True
    except Exception:
        stack_exists = False
        print("First run for docker configuration, proceeding to build...")
and ive done it with the create_or_select
Copy code
print("initializing stack...")
        stack = pulumi.automation.create_or_select_stack(stack_name=stack_name_fqdn, work_dir=work_dir)

        print("setting up stack config...")
        stack.set_config("aws:region", pulumi.automation.ConfigValue(value=region))
either works.. but we always use the FQDN stack name
b
I'm wondering if I need the automation API to actually setup the project first? I just asked the Pulumi AI about defining an Org and organizing workspaces and there's a bunch of code I'm missing about setting up the project for each app.
r
Have you looked at the automation-api-examples repo? https://github.com/pulumi/automation-api-examples/tree/main/python/local_program might be helpful?
b
@red-match-15116 yes, and @sparse-gold-10561 provided me with another example he built, following the same general concept. In short, I had not initialized the project or provided any LocalWorkspace options. Once I setup the Workspace_opts, everything started working like a charm.
Copy code
def main():
    """Setup the Pulumi application environment"""
    appName="simple-app"
    factoryId="CL1004"
    stackEnv="dev"
    region="us-east-1"
    vpcID="<redacted>"
    numAZs="3"
    dbType="docdb"
    if appName and factoryId:
        projName=factoryId+ "-" + appName
    else:
        return "Please provide both an application name and a factory ID."
    os.environ['PULUMI_CONFIG_PASSPHRASE'] = ''

# Create working directory for the application stacks (environments)
    path = projName
    if not os.path.exists(path):
        try:
            pathlib.Path(path).mkdir(parents=True)
            work_dir=str(path)
        except FileExistsError:
            "Application folder already exists."
            work_dir=str(path)
    else:
        work_dir=str(path)

    # Setup the workspace & stack
    workspace_opts = auto.LocalWorkspaceOptions(
        project_settings=auto.ProjectSettings(name=projName, runtime="python"),
        work_dir=work_dir
    )
    stack_fqdn = auto.fully_qualified_stack_name(org="organization", project=projName, stack=stackEnv)
    
    # Select or setup the stack
    try:
        stack = auto.create_or_select_stack(
         stack_name=stack_fqdn,
         project_name=projName,
         program=pulumi_program,
         opts=workspace_opts
        )
        stack.workspace.install_plugin("aws", "6.56.1")
        stack.set_config("aws:region", auto.ConfigValue(value=region))
        stack.set_config("aws:profile", auto.ConfigValue(value="<redacted>"))
        stack.set_config("vpcID", auto.ConfigValue(value=vpcID))
        stack.set_config("numAZs", auto.ConfigValue(value=numAZs))
        stack.set_config("dbType", auto.ConfigValue(value=dbType))
        stack.set_config("stankEnv", auto.ConfigValue(value=stackEnv))
        print("Checking for any changes made outside of IaC.")
        stack.refresh(on_output=print, color="always")
    except auto.StackAlreadyExistsError:
        print(f"Stack '{stackEnv}' for '{projName}' already exists.")
    except auto.RuntimeError as e:
        print(f"Runtime error: {e}")
    except auto.RuntimeError as e:
        print(f"Runtime error: {e}")
r
awesome! glad you got it working