private getOrCreateResource()

in packages/aws-cdk-lib/cloudformation-include/lib/cfn-include.ts [627:756]


  private getOrCreateResource(logicalId: string, cycleChain: string[] = []): core.CfnResource {
    cycleChain = cycleChain.concat([logicalId]);
    if (cycleChain.length !== new Set(cycleChain).size) {
      if (!this.allowCyclicalReferences) {
        throw new core.ValidationError(`Found a cycle between resources in the template: ${cycleChain.join(' depends on ')}`, this);
      }
      // only allow one placeholder per logical id
      if (this.logicalIdToPlaceholderMap.get(logicalId)) {
        return this.resources[this.logicalIdToPlaceholderMap.get(logicalId)!];
      }
      let placeholderResourceAttributes: any = this.template.Resources[logicalId];
      let placeholderId: string = this.getPlaceholderID();
      this.logicalIdToPlaceholderMap.set(logicalId, placeholderId);
      let placeholderInstance = new core.CfnResource(this, placeholderId, {
        type: placeholderResourceAttributes.Type,
        properties: {},
      });
      placeholderInstance.overrideLogicalId(placeholderId);
      this.resources[placeholderId] = placeholderInstance;

      return placeholderInstance;
    }

    const ret = this.resources[logicalId];
    if (ret) {
      return ret;
    }

    const self = this;
    const finder: cfn_parse.ICfnFinder = {
      findCondition(conditionName: string): core.CfnCondition | undefined {
        return self.conditions[conditionName];
      },

      findMapping(mappingName): core.CfnMapping | undefined {
        return self.mappings[mappingName];
      },

      findResource(lId: string): core.CfnResource | undefined {
        if (!(lId in (self.template.Resources || {}))) {
          return undefined;
        }
        return self.getOrCreateResource(lId, cycleChain);
      },

      findRefTarget(elementName: string): core.CfnElement | undefined {
        if (elementName in self.parameters) {
          return self.parameters[elementName];
        }

        return this.findResource(elementName);
      },
    };
    const cfnParser = new cfn_parse.CfnParser({
      finder,
      parameters: this.parametersToReplace,
    });

    const resourceAttributes: any = this.template.Resources[logicalId];
    let l1Instance: core.CfnResource;
    if (this.nestedStacksToInclude[logicalId] && this.dehydratedResources.includes(logicalId)) {
      throw new core.ValidationError(`nested stack '${logicalId}' was marked as dehydrated - nested stacks cannot be dehydrated`, this);
    } else if (this.nestedStacksToInclude[logicalId]) {
      l1Instance = this.createNestedStack(logicalId, cfnParser);
    } else if (this.dehydratedResources.includes(logicalId)) {
      l1Instance = new core.CfnResource(this, logicalId, {
        type: resourceAttributes.Type,
        properties: resourceAttributes.Properties,
      });
      const cfnOptions = l1Instance.cfnOptions;
      cfnOptions.creationPolicy = resourceAttributes.CreationPolicy;
      cfnOptions.updatePolicy = resourceAttributes.UpdatePolicy;
      cfnOptions.deletionPolicy = resourceAttributes.DeletionPolicy;
      cfnOptions.updateReplacePolicy = resourceAttributes.UpdateReplacePolicy;
      cfnOptions.version = resourceAttributes.Version;
      cfnOptions.description = resourceAttributes.Description;
      cfnOptions.metadata = resourceAttributes.Metadata;
      this.resources[logicalId] = l1Instance;

      return l1Instance;
    } else {
      const l1ClassFqn = cfn_type_to_l1_mapping.lookup(resourceAttributes.Type);
      // The AWS::CloudFormation::CustomResource type corresponds to the CfnCustomResource class.
      // Unfortunately, it's quite useless; it only has a single property, ServiceToken.
      // For that reason, even the CustomResource class from @core doesn't use it!
      // So, special-case the handling of this one resource type
      if (l1ClassFqn && resourceAttributes.Type !== 'AWS::CloudFormation::CustomResource') {
        const options: cfn_parse.FromCloudFormationOptions = {
          parser: cfnParser,
        };
        const [moduleName, ...className] = l1ClassFqn.split('.');
        const module = require(moduleName); // eslint-disable-line @typescript-eslint/no-require-imports
        const jsClassFromModule = module[className.join('.')];
        l1Instance = jsClassFromModule._fromCloudFormation(this, logicalId, resourceAttributes, options);
      } else {
        l1Instance = new core.CfnResource(this, logicalId, {
          type: resourceAttributes.Type,
          properties: cfnParser.parseValue(resourceAttributes.Properties),
        });
        cfnParser.handleAttributes(l1Instance, resourceAttributes, logicalId);
      }
    }

    /*
    1. remove placeholder version of object created for cycle breaking
    2. override logical id before deletion so references to the placeholder instead reference the original
     */
    if (this.logicalIdToPlaceholderMap.get(logicalId)) {
      let placeholderId: string = this.logicalIdToPlaceholderMap.get(logicalId)!;
      this.resources[placeholderId].overrideLogicalId(logicalId);
      this.node.tryRemoveChild(placeholderId);
      delete this.resources[placeholderId];
    }

    this.overrideLogicalIdIfNeeded(l1Instance, logicalId);
    this.resources[logicalId] = l1Instance;

    // handle any unknown attributes using overrides
    const knownAttributes = [
      'Condition', 'DependsOn', 'Description', 'Metadata', 'Properties', 'Type', 'Version',
      'CreationPolicy', 'DeletionPolicy', 'UpdatePolicy', 'UpdateReplacePolicy',
    ];
    for (const [attrName, attrValue] of Object.entries(resourceAttributes)) {
      if (!knownAttributes.includes(attrName)) {
        l1Instance.addOverride(attrName, cfnParser.parseValue(attrValue));
      }
    }

    return l1Instance;
  }